二叉搜索树(BST)基本操作

什么是二叉搜索树?

二叉搜索树也叫做二叉排序树、二叉查找树,它有以下性质:

  1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 任意节点的左、右子树也分别为二叉查找树;
  4. 没有键值相等的节点。

二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低,为O(log n)。

基本操作

1.定义BST对象

public class BST<E extends Comparable<E>> {


    /**
     * 定义节点
     */
    private class Node {
        private E e;
        private Node left;
        private Node right;

        Node(E e) {
            this.e = e;
        }
    }

    private Node root;
    private int size;

    /**
     * 获取节点数
     *
     * @return
     */
    public int size() {
        return this.size;
    }
}

2.添加节点

1.采用递归,终止条件是当前节点为null时,创建新的节点;
2.如果当前节点不为null,则通过比较插入值和当前节点值的大小,递归其左/右子节点。


public void add(E element){
    root = add(root, element);
}

private void _add(Node node, E element){
    if(node == null){
        size++;
        return new Node(element);
    }
    
    if(element.compareTo(node.e) > 0){
        node.right = _add(node.right, element);
    }else if(element.compareTo(node.e) < 0){
        node.left = _add(node.left, element);
    }
    
    return node;
}

3.查询节点

1.通过比较当前节点值的大小,递归查找左/右子节点。


public boolean contains(E element){
    return _contains(root, element);
}

private boolean _contains(Node node,E element){
    if(node == null) return false;
    
    if(element.compareTo(node.e) == 0){
        return true;
    }else if(element.compareTo(node.e) > 0){
        return _contains(node.right, element);
    }else{
        return _contains(node.left, element);
    }
    
    return false;
}

4.打印二叉树

1.通过递增树的深度dept,层级打印;
2.叶子节点通过##表示。


public String toString(){
    StringBuilder builder = new StringBuilder();
    _toString(root, 0, builder);
    return builder.toString();
}

private void _toString(Node node, int dept, StringBuilder builder){
    if(int i=0; i<dept; i++){
        builder.append("--");
    }
    
    if(node == null){
        builder.append("##\n");
        return;
    }
    
    builder.append(node.e).append("\n");
    _toString(node.left, dept+1, builder);
    _toString(right, dept+1, builder);
}

测试运行


public static void main(String[] args) {

        BST<Integer> bst = new BST<>();

        bst.add(30);
        bst.add(10);
        bst.add(22);
        bst.add(50);
        bst.add(18);
        bst.add(34);
        bst.add(5);
        
        System.out.println(bst);
    }

输出结果为:

30
--10
----5
------##
------##
----22
------18
--------##
--------##
------##
--50
----34
------##
------##
----##

遍历操作

二叉树的遍历分为:前序遍历、中序遍历、后序遍历,

这里的前中后指的是父节点的顺序,所以三种遍历的顺序是:

  • 前序遍历(父-左-右)
  • 中序遍历 (左-父-右)
  • 后序遍历 (左-右-父)

1.前序遍历

1.递归遍历,终止条件是当前节点为null;
2.先遍历输出父节点,再遍历左、右子节点


public void preOrder(){
    StringBuilder builer = new StringBuilder();
    _preOrder(root, builer);
    System.out.println(builer.toString);
}

private void _preOrder(Node node,StringBuilder builder){
    if(node == null) return;
    
    builder.append(node.e).append(" ");
    _preOrder(node.left, builder);
    _preOrder(node.right, builder);
}

2.前序遍历2(非递归)

1.借助栈,首先将节点放入栈中,开始遍历栈;
2.遍历取出节点后再将其右子节点和左子节点放入压入栈中;

1

2

3

4


public void preOrder2(){
    StringBuilder builder = new StringBuilder();
    
    Stack<Node> stack = new Stack();
    stack.push(root);
    
    while(!stack.isEmpty()){
        Node node = stack.pop();
        if(node == null) continue;
        
        builder.append(node.e).append(" ");
        stack.push(node.right);
        stack.push(node.left);
    }
    
    System.out.println(builder.toString());
}

3.中序遍历

1.同前序遍历,只是输出的顺序不同


public void inOrder(){
    StringBuilder builer = new StringBuilder();
    _inOrder(root, builer);
    System.out.println(builer.toString);
}

private void _inOrder(Node node,StringBuilder builder){
    if(node == null) return;
    
    _inOrder(node.left, builder);
    builder.append(node.e).append(" ");
    _inOrder(node.right, builder);
}

输入你会发现,二叉搜索树的中序遍历的结果是有序的(从小到大)。

4.后序遍历

1.同前序遍历,只是输出的顺序不同


public void postOrder(){
    StringBuilder builer = new StringBuilder();
    _postOrder(root, builer);
    System.out.println(builer.toString);
}

private void _postOrder(Node node,StringBuilder builder){
    if(node == null) return;
    
    _postOrder(node.left, builder);
    _postOrder(node.right, builder);
    builder.append(node.e).append(" ");
}

5.层序遍历

层序遍历指的是从上到下,从左到右一层层遍历,其遍历方法和上面的前序遍历类似,只是这里借助的是队列;
1.需要借助队列,首选将根节点入队,开始遍历队列;
2.如果当前节点不为null,则将其左子节点和右子节点入队,继续遍历;


public void levelOrder() {
        StringBuilder builder = new StringBuilder();

        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            Node node = queue.poll();

            if (node == null) continue;

            builder.append(node.e).append(" ");

            queue.offer(node.left);
            queue.offer(node.right);
        }

        System.out.println(builder.toString());
    }

删除

1.删除最小/最大节点

以删除最小为例,最小节点一定是树的最左边的那个:
1.因为删除的节点可能是遍历的当前节点,所以需要通过return的形式返回节点;
2.判断左子节点是否为null:
如果不为null则递归遍历左子节点;
如果为null则要删除的是当前节点,要把它的右节点返回;

public Node removeMinimum() {
        root = _removeMinimun(root);
        return root;
    }

private Node _removeMinimun(Node node) {
    if (node == null) return null;

    if (node.left == null) {
        //如果左子节点为null,则把右子节点返回
        Node tmp = node.right;
        node.right = null;
        size--;
        return tmp;
    }

    node.left = _removeMinimun(node.left);
    return node;
}

2.删除任意节点

分三种情况:

  • 如果该节点没有左子节点:返回其右子节点;
  • 如果该节点没有右子节点:返回其左子节点;
  • 如果该节点同时拥有左右子节点:

节点的后继节点是大于它并最接近它的那个节点,也就是该节点的右子树中最小的节点,就是其右子树中最左边的那个,如下图中15的后继节点是17。

1.需要找到其后继节点;
2.将后继节点删除;
3.将后继节点的左子树等于该节点的左子树;
4.将后继节点的右子树等于该节点的右子树删除后继节点返回的树。


    /**
     * 删除任意节点,并返回删除后的根节点
     *
     * @param e
     * @return
     */
    public void removeNode(E e) {
        root = _removeNode(root, e);
    }

    private Node _removeNode(Node node, E e) {
        if (node == null) return null;


        if (e.compareTo(node.e) < 0) {
            node.left = _removeNode(node.left, e);
        } else if (e.compareTo(node.e) > 0) {
            node.right = _removeNode(node.right, e);
        } else {
            //1.查找到对应的节点

            //2.左子树为空的情况,直接把右子树返回
            if (node.left == null) {
                Node right = node.right;
                node.right = null;
                return right;
            }

            //3.右子树为空的情况,直接把左子树放回
            if (node.right == null) {
                Node left = node.left;
                node.left = null;
                return left;
            }

            //4.找到后继节点(右子树中最小的节点)
            Node successor = _mininum(node.right);
            //5.将右子树中最小的节点删除
            successor.right = _removeMinimun(node.right);
            //6.将节点的左子树等于后继节点的左子树
            successor.left = node.left;

            node.left = node.right = null;

            //7.返回后继节点
            return successor;
        }

        return node;
    }

    /**
     * 查找最小的节点
     *
     * @param node
     * @return
     */
    private Node _mininum(Node node) {
        if (node == null) return null;

        if (node.left != null) {
            return _mininum(node.left);
        }

        return node;
    }

完整代码BST.java

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

/**
 * Binary Search Tree
 *
 * @param <E>
 */
public class BST<E extends Comparable<E>> {


    /**
     * 定义节点
     */
    private class Node {
        private E e;
        private Node left;
        private Node right;

        Node(E e) {
            this.e = e;
        }
    }

    private Node root;
    private int size;

    /**
     * 获取节点数
     *
     * @return
     */
    public int size() {
        return this.size;
    }

    /**
     * 添加节点
     *
     * @param element
     */
    public void add(E element) {

        root = add(root, element);
    }

    private Node add(Node node, E element) {

        if (node == null) {
            size++;
            return new Node(element);
        }

        if (element.compareTo(node.e) > 0) {
            node.right = add(node.right, element);
        } else if (element.compareTo(node.e) < 0) {
            node.left = add(node.left, element);
        }

        return node;
    }

    /**
     * 是否含有节点
     *
     * @param element
     * @return
     */
    public boolean contains(E element) {

        return contains(root, element);
    }

    private boolean contains(Node node, E element) {
        if (node == null) return false;

        if (element.equals(node.e)) return true;

        if (element.compareTo(node.e) < 0) {
            return contains(node.left, element);
        } else {
            return contains(node.right, element);
        }

    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();

        toString(root, 0, builder);

        return builder.toString();
    }

    private void toString(Node node, int dept, StringBuilder builder) {
        for (int i = 0; i < dept; i++) {
            builder.append("--");
        }

        if (node == null) {
            builder.append("##\n");
            return;
        }

        builder.append(node.e);
        builder.append("\n");

        toString(node.left, dept + 1, builder);
        toString(node.right, dept + 1, builder);
    }

    /**
     * 前序遍历
     */
    public void preOrder() {
        StringBuilder builder = new StringBuilder();

        preOrder(root, builder);

        System.out.println(builder.toString());
    }

    private void preOrder(Node root, StringBuilder builder) {
        if (root == null) return;

        builder.append(root.e).append(" ");
        preOrder(root.left, builder);
        preOrder(root.right, builder);
    }

    /**
     * 前序遍历(非递归)
     */
    public void preOrderTraverse2() {
        StringBuilder builder = new StringBuilder();

        Stack<Node> stack = new Stack<>();
        stack.push(root);

        while (!stack.isEmpty()) {
            Node node = stack.pop();
            if (node == null) continue;

            builder.append(node.e).append(" ");

            stack.push(node.right);
            stack.push(node.left);
        }

        System.out.println(builder.toString());
    }

    /**
     * 中序遍历
     */
    public void inOrder() {
        StringBuilder builder = new StringBuilder();

        inOrder(root, builder);
        System.out.println(builder.toString());
    }

    private void inOrder(Node root, StringBuilder builder) {
        if (root == null) return;

        inOrder(root.left, builder);
        builder.append(root.e).append(" ");
        inOrder(root.right, builder);
    }

    /**
     * 后序遍历
     */
    public void postOrder() {
        StringBuilder builder = new StringBuilder();

        postOrder(root, builder);
        System.out.println(builder.toString());
    }

    private void postOrder(Node root, StringBuilder builder) {
        if (root == null) return;

        postOrder(root.left, builder);
        postOrder(root.right, builder);
        builder.append(root.e).append(" ");
    }

    /**
     * 层序遍历
     */
    public void levelOrder() {
        StringBuilder builder = new StringBuilder();

        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            Node node = queue.poll();

            if (node == null) continue;

            builder.append(node.e).append(" ");

            queue.offer(node.left);
            queue.offer(node.right);
        }

        System.out.println(builder.toString());
    }

    /**
     * 删除最小节点,返回删除后的根节点
     */
    public Node removeMinimum() {
        root = _removeMinimun(root);
        return root;
    }

    private Node _removeMinimun(Node node) {
        if (node == null) return null;

        if (node.left == null) {
            //如果左子节点为null,则把右子节点返回
            Node tmp = node.right;
            node.right = null;
            size--;
            return tmp;
        }

        node.left = _removeMinimun(node.left);
        return node;
    }

    /**
     * 删除最大节点,,返回删除后的根节点
     *
     * @return
     */
    public Node removeMaximum() {
        root = _removeMaxinum(root);
        return root;
    }

    private Node _removeMaxinum(Node node) {
        if (node == null) return null;

        if (node.right == null) {
            Node tmp = node.left;
            node.left = null;
            size--;
            return tmp;
        }

        node.right = _removeMaxinum(node.right);
        return node;
    }

    /**
     * 删除任意节点,并返回删除后的根节点
     *
     * @param e
     * @return
     */
    public void removeNode(E e) {
        root = _removeNode(root, e);
    }

    private Node _removeNode(Node node, E e) {
        if (node == null) return null;


        if (e.compareTo(node.e) < 0) {
            node.left = _removeNode(node.left, e);
        } else if (e.compareTo(node.e) > 0) {
            node.right = _removeNode(node.right, e);
        } else {
            //1.查找到对应的节点

            //2.左子树为空的情况,直接把右子树返回
            if (node.left == null) {
                Node right = node.right;
                node.right = null;
                return right;
            }

            //3.右子树为空的情况,直接把左子树放回
            if (node.right == null) {
                Node left = node.left;
                node.left = null;
                return left;
            }

            //4.找到后继节点(右子树中最小的节点)
            Node successor = _mininum(node.right);
            //5.将右子树中最小的节点删除
            successor.right = _removeMinimun(node.right);
            //6.将节点的左子树等于后继节点的左子树
            successor.left = node.left;

            node.left = node.right = null;

            //7.返回后继节点
            return successor;
        }

        return node;
    }

    /**
     * 查找最小的节点
     *
     * @param node
     * @return
     */
    private Node _mininum(Node node) {
        if (node == null) return null;

        if (node.left != null) {
            return _mininum(node.left);
        }

        return node;
    }

}

猜你喜欢

转载自www.cnblogs.com/monsterdev/p/11191846.html
今日推荐