• 二叉搜索树(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("##
    ");
            return;
        }
        
        builder.append(node.e).append("
    ");
        _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("##
    ");
                return;
            }
    
            builder.append(node.e);
            builder.append("
    ");
    
            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;
        }
    
    }
    
  • 相关阅读:
    jackson 解析json含有不规则的属性的json字符串的方法
    swift入门-实现简单的登录界面
    github git.exe位置
    linux之SQL语句简明教程---IN
    怎么样才算是精通 C++?
    BZOJ2028: [SHOI2009]会场预约(set)
    BZOJ1058: [ZJOI2007]报表统计(set)
    洛谷P2391 白雪皑皑(并查集)
    BZOJ4514: [Sdoi2016]数字配对(费用流)
    BZOJ3143: [Hnoi2013]游走(期望DP 高斯消元)
  • 原文地址:https://www.cnblogs.com/monsterdev/p/11191846.html
Copyright © 2020-2023  润新知