二叉排序树

2022-05-13 15:06:44 浏览数 (1)

1. 是什么?

数组和链表在增删改查数据时,都有各自的缺点,比如数组要在中间插入数据,就要让后面的数据整体都移动,而链表检索数据很慢。之前说二叉树时,说到树这种结构就是就是为了弥补数组和链表的缺点而诞生的,二叉排序树(Binary search tree,简称BST),更是体现了这一点。二叉排序树有以下特点:

  • 左子节点的值比父节点小;
  • 右子节点的值比父节点大;

2. 构建过程:

假如现有数列:7,3,10,12,5,1,9,构建二叉排序树的过程如下:

  • 7当成父节点,3比7小,放到7的左边;
  • 10比7大,放到7的右边;
  • 12比7大,比10也大,放到10的右边;
  • 5比7小,比3大,放到3的右边;
  • 1比7小,比3小,放到3的左边;
  • 9比7大,比10小,放到10的左边;

二叉排序树

3. 二叉排序树的删除:

二叉排序树的删除有三种情况,如下:

(1):删除的是叶子节点:比如上面这棵二叉排序树中的1,5,9,12:

  • 首先找到这个节点targetNode,如果不存在,直接删除失败;
  • 然后找到targetNode节点的父节点parent;
  • 然后判断targetNode是它的左节点还是右节点,直接让对应的左/右节点置空就行了(为什么不能直接targetNode == null?因为这个时候targetNode其实只是一个临时指针,并不是原二叉树的节点,targetNode == null对原二叉树是没有影响的)。

(2):删除的是只有一棵子树的节点:假如上面这棵二叉排序树节点1右边再挂个2,那么1就是只有一棵子树:

  • 前面的逻辑都一样,找到targetNode的parentNode后,要判断targetNode的子节点sonNode是在targetNode的左边还是右边,同时要判断targetNode在parent的左边还是右边。
  • 如果targetNode是parentNode的左子节点,那么直接将sonNode挂在parentNode左边即可;
  • 如果targetNode是parentNode的右子节点,那么直接将sonNode挂在parentNode右边即可;
  • 如果parentNode为空,说明要删除的是根节点,并且根节点只有一个子节点,那么直接把这个子节点的值赋给根节点,然后再置空子节点即可。

(3):删除的是有两棵子树的节点:比如上面这棵二叉排序树中的7,3,10:

  • 前面的逻辑也一样,找到targetNode和parentNode;
  • 从targetNode的右子树找到最小的节点,用临时变量temp保存起来;
  • 删除上一步找到的那个右子树的最小节点;
  • 将temp的值赋给targeteNode。

4. 代码实现:

按照上面的思路,用代码实现二叉排序树的构建和删除功能:

代码语言:javascript复制
public class BinarySearchTree {

    /** 根节点 */
    private Node root;

    /**
     * 添加节点
     * @param value
     */
    public void add(int value){
        if (root == null){
            root = new Node(value);
        } else {
            root.add(new Node(value));
        }
    }

    /**
     * 查找值为value的节点
     * @param value
     * @return
     */
    public Node findNode(int value){
        if (root == null){
            return null;
        } else {
            return root.findNode(value);
        }
    }

    /**
     * 查找值为value的节点的父节点
     * @param value
     * @return
     */
    public Node findParentNode(int value){
        if (root == null){
            return null;
        } else {
            return root.findParentNode(value);
        }
    }

    /**
     * 删除值为value的节点
     * @param value
     */
    public void deleteNode(int value){
        if (root == null){
            return;
        }
        // 找到要删除的节点和要删除节点的父节点
        Node targetNode = findNode(value);
        Node parentNode = findParentNode(value);
        if (targetNode == null){
            return;
        }
        if (targetNode.left == null && targetNode.right == null){
            // 要删除的是叶子节点
            deleteLeafNode(targetNode, parentNode);
        } else if(targetNode.left != null && targetNode.right != null){
            // 要删除的是有两棵子树的节点
            deleteDoubleSonNode(targetNode);
        } else {
            // 要删除的是只有一棵子树的节点
            deleteSingleSonNode(targetNode, parentNode);
        }
    }

    /**
     * 要删除的节点是叶子节点
     * @param targetNode
     * @param parentNode
     */
    private void deleteLeafNode(Node targetNode, Node parentNode){
        if (parentNode == null){
            // parentNode为空,说明整棵二叉树只有一个节点,直接置空root即可
            root = null;
        } else {
            // parentNode不为空,那就看targetNode是parentNode的左孩子还是右孩子
            if (parentNode.left != null && parentNode.left.value == targetNode.value){
                parentNode.left = null;
            } else {
                parentNode.right = null;
            }
        }
    }

    /**
     * 要删除的是有两棵子树的节点
     * @param targetNode
     */
    private void deleteDoubleSonNode(Node targetNode){
        // 从targetNode的右子树找到值最小的节点
        Node rightMinNode = findMinNode(targetNode.right);
        // 删除找到的最小的节点,此时的rightMinNode一定是叶子节点
        deleteNode(rightMinNode.value);
        // 将rightMinNode的值赋给targetNode
        targetNode.value = rightMinNode.value;
    }

    /**
     * 要删除的是只有一棵子树的节点
     * @param targetNode
     * @param parentNode
     */
    private void deleteSingleSonNode(Node targetNode, Node parentNode){
        if (parentNode == null){
            // 要删除的是根节点,且根节点有一棵子树
            Node sonNode = root.left == null ? root.right : root.left;
            root.value = sonNode.value;
            root.left = null;
            root.right = null;
        } else {
            Node sonNode = targetNode.left == null ? targetNode.right : targetNode.left;
            // 要删除的节点有一棵子树且不是根节点
            if (parentNode.left != null && parentNode.left.value == targetNode.value){
                parentNode.left = sonNode;
            } else {
                parentNode.right = sonNode;
            }
        }
    }

    /**
     * 查找node子树中值最小的节点
     * @param node
     * @return
     */
    private Node findMinNode(Node node){
        Node rightMinNode = node;
        while (rightMinNode.left != null){
            rightMinNode = rightMinNode.left;
        }
        return rightMinNode;
    }

    /**
     * 中序遍历
     */
    public void middleOrder(){
        if (root == null){
            return;
        } else {
            root.middleOrder();
        }
    }

    /**
     * 层序遍历
     */
    public void sequenceOrder(){
        if (root == null){
            return;
        } else {
            root.sequenceOrder();
        }
    }

    /**
     * 节点
     */
    class Node{
        /** 值 */
        int value;
        /** 左子树 */
        Node left;
        /** 右子树 */
        Node right;
        Node(int value){
            this.value = value;
        }

        /**
         * 添加节点
         * @param node
         */
        public void add(Node node){
            if (node == null){
                return;
            }
            if (node.value < this.value){
                // 值比当前节点小,往左添加
                if (this.left == null){
                    this.left = node;
                } else {
                    this.left.add(node);
                }
            } else {
                // 值比当前节点大,往右添加
                if (this.right == null){
                    this.right = node;
                } else {
                    this.right.add(node);
                }
            }
        }

        /**
         * 查找值为value的节点
         * @param value
         * @return
         */
        public Node findNode(int value){
            if (this.value == value){
                return this;
            } else if (value < this.value && this.left != null){
                return this.left.findNode(value);
            } else if (value >= this.value && this.right != null){
                return this.right.findNode(value);
            } else {
                return null;
            }
        }

        /**
         * 查找值为value的节点的父节点
         * @param value
         * @return
         */
        public Node findParentNode(int value){
            if ((this.left != null && this.left.value == value)
                    || (this.right != null && this.right.value == value)){
                return this;
            } else {
                if (this.left != null && value < this.value){
                    return this.left.findParentNode(value);
                } else if (this.right != null && value >= this.value){
                    return this.right.findParentNode(value);
                } else {
                    return null;
                }
            }
        }

        /**
         * 中序遍历
         */
        public void middleOrder(){
            if (this.left != null){
                this.left.middleOrder();
            }
            System.out.println(this.value);
            if (this.right != null){
                this.right.middleOrder();
            }
        }

        /**
         * 层序遍历
         */
        public void sequenceOrder(){
            Queue<Node> queue = new LinkedList<>();
            Node currentNode = this;
            while(currentNode != null){
                System.out.println(currentNode.value);
                if (currentNode.left != null){
                    queue.add(currentNode.left);
                }
                if (currentNode.right != null){
                    queue.add(currentNode.right);
                }
                currentNode = queue.poll();
            }
        }
    }
}

测试代码:

代码语言:javascript复制
public class BinarySearchTreeTest {

    public static void main(String[] args) {
        int[] arr = {7,3,10,12,5,1,9};
        BinarySearchTree binarySearchTree = new BinarySearchTree();
        for (int i = 0; i < arr.length; i  ) {
            binarySearchTree.add(arr[i]);
        }
        binarySearchTree.middleOrder();
        binarySearchTree.deleteNode(5);
        System.out.println("删除之后:");
        binarySearchTree.middleOrder();
    }
}

不知各位发现没有,二叉排序树中序遍历就是一个升序序列

0 人点赞