31-二叉排序树

1. 引入

给你一个数列 {7, 3, 10, 12, 5, 1, 9},要求能够高效的完成对数据的查询和添加。

1. 使用数组

  • 数组未排序
    • 优点:直接在数组尾部添加,速度快
    • 缺点:查找速度慢
  • 数组排序
    • 优点:可以使用二分查找,查找速度快
    • 缺点:为了保证数组有序,在添加新数据时,找到插入位置后,后面的数据需整体移动,速度慢

2. 使用链表

不管链表是否有序,查找速度都慢,添加数据速度比数组快,不需要数据整体移动

3. 使用二叉排序树

2. BST 介绍

  • 二叉排序/搜索树(Binary Sort/Search Tree,简称 BST),对于二叉排序树的任何一个非叶子结点,要求:
    • 左子结点的值比当前结点的值小
    • 右子结点的值比当前结点的值大
  • 特别说明:如果有相同的值,可以将该节点放在左子结点或右子结点
  • 比如针对前面的数据 {7, 3, 10, 12, 5, 1, 9},对应的二叉排序树为:

3. BST 删除结点

3.1 删除叶子结点

  1. 找到待删除的结点 targetNode
  2. 找到 targetNode 的父结点
  3. 确定 targetNode 是 parent 的左子结点,还是右子结点
  4. 根据 step3 来进行删除

3.2 删除只有一颗子树的结点

  1. 找到待删除的结点 targetNode
  2. 找到 targetNode 的父结点 parent
  3. 确定 targetNode 是父结点的左子结点还是右子结点
  4. 确定 targetNode 的子结点是左子结点还是右子结点
  5. 分 4 种情况
    // 父之左 & 子在左
    parent.left = targetNode.left;
    // 父之左 & 子在右
    parent.left = targetNode.right;
    // 父之右 & 子在左
    parent.right = targetNode.left;
    // 父之右 & 子在右
    parent.right = targetNode.right;
    

3.3 删除有两颗子树的结点

  1. 找到待删除的结点 targetNode
  2. 找到 targetNode 的父结点 parent
  3. 从 targetNode 的右(左)子树中找出 value 最小(大)的结点
  4. 用一个临时变量 temp 来保存该最小(大)结点的 value
  5. 删除该最小(大)结点,并让 targetNode.value = temp // 可以理解为:用 targetNode 的右(左)子树中的最小(大)结点来替代 targetNode

4. 代码实现

class BinarySortTree {
    Node root;

    public void add(Node node) {
        if (root == null) root = node;
        else root.add(node);
    }

    public void infixOrder() {
        if (root != null) root.infixOrder();
        else System.out.println("空树");
        System.out.println();
    }

    public Node searchNode(int value) {
        if (root == null) return null;
        else return root.searchNode(value);
    }

    public Node searchParent(int value) {
        if (root == null || value == root.value) return null;
        else return root.searchParent(value);
    }

    /**
     * 删除node的最小value子结点, 并返回该最小结点的value
     * @param node 二叉排序树的根结点
     * @return 以node为根结点的二叉排序树的最小值
     */
    public int delTreeMinNode(Node node) {
        Node target = node;
        // 循环的查找左子结点
        while (target.left != null) target = target.left;
        // 这时, target就指向了最小结点
        delNode(target.value);
        return target.value;
    }

    /**
     * 删除node的最大value子结点, 并返回该最大结点的value
     * @param node 二叉排序树的根结点
     * @return 以node为根结点的二叉排序树的最小值
     */
    public int delTreeMaxNode(Node node) {
        Node target = node;
        // 循环的查找右子结点
        while (target.right != null) target = target.right;
        // 这时, target就指向了最小结点
        delNode(target.value);
        return target.value;
    }

    public void delNode(int value) {
        if (root == null) return;

        // 1. 找到待删除结点
        Node targetNode = searchNode(value);
        // a. 待删除结点不存在
        if (targetNode == null) return;
        // b. 存在, 是根结点,且这颗树只有这 1 个结点
        if (root.left == null && root.right == null) {
            root = null;
            return;
        }
        // 2. 找待删除结点的父结点
        Node parent = searchParent(value);
        // 3. 删除
        if (targetNode.left == null && targetNode.right == null) { // 3.1 叶子结点
            // 判断 targetNode 是 parent 的左子结点还是右子结点
            if (parent.left != null && parent.left.value == value)
                parent.left = null;
            else if (parent.right != null && parent.right.value == value)
                parent.right = null;
        } else if (targetNode.left != null && targetNode.right != null) { // 3.2 双子结点
            // a. 删除 targetNode 的右子树中 value 最小的结点, 并将其 value 赋值给 targetNode
            targetNode.value = delTreeMinNode(targetNode.right);
            // b. 删除 targetNode 的左子树中 value 最大的结点, 并将其 value 赋值给 targetNode
            // targetNode.value = delTreeMaxNode(targetNode.left);
        } else { // 3.3 单子结点
            if (parent.left != null && parent.left.value == value) { // 父之左
                if (targetNode.left != null) { // 子在左
                    parent.left = targetNode.left;
                } else { // 子在右
                    parent.left = targetNode.right;
                }
            } else { // 父之右
                if (targetNode.left != null) { // 子在左
                    parent.right = targetNode.left;
                } else { // 子在右
                    parent.right = targetNode.right;
                }
            }
        }
    }
}

class Node {
    int value;
    Node left;
    Node right;

    public Node(int value) {
        super();
        this.value = value;
    }

    // 查找结点
    public Node searchNode(int value) {
        if (value == this.value) {
            return this;
        } else if(value < this.value) {
            if (this.left != null) return this.left.searchNode(value);
            else return null;
        } else {
            if (this.right != null) return this.right.searchNode(value);
            else return null;
        }
    }

    /**
     * 查找某个结点的父结点
     * @param value 子结点的value
     * @return 父结点
     */
    public Node searchParent(int value) {
        if ((this.left != null && this.left.value == value)
                || (this.right != null && this.right.value == value)) {
            return this;
        } else {
            if (value < this.value && this.left != null)
                return this.left.searchParent(value);
            else if (value > this.value && this.right != null)
                return this.right.searchParent(value);
            else
                return null;
        }
    }

    // (递归)添加结点
    public void add(Node node) {
        if (node == null) return;
        // 判断node的value和当前子树根结点的value的大小关系
        if (node.value < this.value) {
            if (this.left != null) his.left.add(node);
            else this.left = node;
        } else {
            if (this.right != null) this.right.add(node);
            else this.right = node;
        }
    }

    // 中序遍历
    public void infixOrder() {
        if (this.left != null) this.left.infixOrder();
        System.out.print(this + " ");
        if (this.right != null) this.right.infixOrder();
    }

    @Override
    public String toString() {
        return "[value=" + value + "]";
    }
}
原文地址:https://www.cnblogs.com/liujiaqi1101/p/12327976.html