Java中的二分搜索树

目录

二分搜索树

引言

基础操作

1. 向BST中添加一个节点

2. 判断val是否在BST中存在contains

3. 按照先序遍历的方式打印当前的BST

4. 找到一颗BST的最大值和最小值 

5. 删除最大值和最小值

6. 删除任意值val


二分搜索树

引言

基础二叉树的衍生结构在实际工程有着广泛的应用
堆——完全二叉树
AVL、RBTree、2-3树、B树——搜索树


BST:二分搜索树(二叉搜索树、二叉排序树),查找操作。
1. 也是个二叉树

2. 每个树的左子树的所有节点值 < 树根节点 < 所有右子树的节点值(当前树中所有子树仍然遵循这个规则)。JDK中的BST,不存在重复节点。

3. 存储结点必须具备可以比较的能力(实现Comparable接口 传入比较器)

基础操作

1. 向BST中添加一个节点

新插入的元素都一定在树的叶子结点进行插入操作

不断和数根节点去比大小,若小就在左树中添加,若大就在右子树添加递归上述过程

    /**
     * 当前以root为根的BST树中插入一个元素val,返回插入后的树根
     */
    private Node add(Node root, int val) {
        if (root == null) {
            //此时树为空,node就是树根
            Node node = new Node(val);
            size++;
            return node;
        }
        if (val < root.val) {
            //在左子树中插入
            root.left = add(root.left, val);
        }
        if (val > root.val) {
            //在右子树中插入
            root.right = add(root.right, val);
        }
        return root;
    }

时间复杂度
平均: O(logn)——树的插入
最坏:O(n)。假设此时插入的是一个完全有序的集合[1,2,3,4,],插入5,此时二分搜索树变为了单枝树,退化成了链表,还是在右侧插入。

2. 判断val是否在BST中存在contains

不断递归的去和树的树根相比

val == root.val,找到了

val < root.val,继续在左树中找

val > root.val,继续在右树中找

root == null,根本不存在这个val
这个查找过程就是—个"二分搜索"

    /**
     * 判断以当前root为根的BST中是否包含指定值val。存在返回true,否则返回false
     */
    private boolean contains(Node root, int val) {
        if (root == null) {
            return false;
        }
        if (val == root.val) {
            return true;
        } else if (val < root.val) {
            //在左子树中找
            return contains(root.left, val);
        } else {
            //在右子树中找
            return contains(root.right, val);
        }
    }

时间复杂度:平均:O(logn)
最坏:退化成链表,单枝树,链表的遍历O(n)

3. 按照先序遍历的方式打印当前的BST

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        generateBSTString(root, 0, sb);
        return sb.toString();
    }

    /**
     * 按照先序遍历将BST的节点值存入sb之中
     */
    private void generateBSTString(Node root, int height, StringBuilder sb) {
        // 边界
        if (root == null) {
            sb.append(generateHeightStr(height)).append("NULL\n");
            return;
        }
        sb.append(generateHeightStr(height)).append(root.val).append("NULL\n");
        // 递归打印左子树
        generateBSTString(root.left,height+1,sb);
        // 递归打印右子树
        generateBSTString(root.right,height+1,sb);
    }

    /**
     * 按照当前节点的高度打印 --
     * root.left => --
     * 第二层 ----
     *
     * @param height
     */
    private String generateHeightStr(int height) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < height; i++) {
            sb.append("--");
        }
        return sb.toString();
    }

4. 找到一颗BST的最大值和最小值 

问题:BST的最大值和最小值一定都在叶子结点?

答:错误

在BST中最小值结点,是先序遍历中第一个左子树为空的结点。

    public int findMin() {
        if (size == 0) {
            throw new NoSuchElementException("bst is empty!no element!");
        }
        return min(root).val;
    }

    /**
     * 以root为根的BST中找到最小值节点
     */
    private Node min(Node root) {
        if (root.left == null) {
            // 这个节点就是第一个碰到左子树为为空的节点
            return root;
        }
        return min(root.left);
    }

在BST中最大值结点,是先序遍历中第一个右子树为空的结点。

    public int findMax() {
        if (size == 0) {
            throw new NoSuchElementException("bst is empty!no element!");
        }
        return max(root).val;
    }

    /**
     * 以root为根的BST中找到最大值节点
     */
    private Node max(Node root) {
        if (root.right == null) {
            // 这个节点就是第一个碰到右子树为为空的节点
            return root;
        }
        return max(root.right);
    }

5. 删除最大值和最小值

删除最小值

    public int removeMin() {
        if (size == 0) {
            throw new NoSuchElementException("bst is empty!cannot remove!");
        }
        Node minNode = min(root);
        root = removeMin(root);
        return minNode.val;
    }

    /**
     * 传入一棵以root为根的BST,就能删除其中的最小值,返回删除后的树根节点
     */
    private Node removeMin(Node root) {
        // 边界
        if (root.left == null) {
            // root就是待删除的节点
            Node right = root.right;
            root.left = root.right = root = null;
            size--;
            return right;
        }
        // 左树删除,更新左子树的引用
        root.left = removeMin(root.left);
        return root;
    }

删除最大值

    public int removeMax() {
        if (size == 0) {
            throw new NoSuchElementException("bst is empty!cannot remove!");
        }
        Node max = max(root);
        root = removeMax(root);
        return max.val;
    }
    /**
     * 删除以root为根的BST中的最大值节点,返回删除后的树根节点
     */
    private Node removeMax(Node root) {
        if (root == null) {
            // root就是待删除的节点
            Node left = root.left;
            root.left = root.right = root = null;
            size--;
            return left;
        }
        // 此时root不是待删除的节点,待删节点一定在右子树删除,
        // 更新右子树的引用
        root.right = removeMax(root.right);
        return root;
    }

6. 删除任意值val

代码实现

    public void remove(int val) {
        // 边界
        if (size == 0) {
            throw new NoSuchElementException("bst is empty!cannot remove!");
        }
        root = remove(root, val);
    }

    /**
     * 在当前以root为根的bst中删除值为val的节点
     * 返回删除后的新的树根节点
     */
    private Node remove(Node root, int val) {
        if (root == null) {
            // 将树中所有节点全都遍历一遍后还没有找到值为val的节点,节点根本不存在
            throw new NoSuchElementException("bst has not this node!");
        } else if (val < root.val) {
            //在左子树中删除
            root.left = remove(root.left, val);
            return root;
        } else if (val > root.val) {
            //在右子树中删除
            root.right = remove(root.right, val);
            return root;
        } else {
            // 树不为空,且root.val == val
            // 当前root就是待删除的节点
            if (root.left == null) {
                // 当前待删除节点的左树为空,类似删除最小值,返回右子树的树根
                Node right = root.right;
                root.right = root = null;
                size--;
                return right;
            }
            if (root.right == null) {
                // 当前待删除节点的右树为空,类似删除最大值,返回左子树的树根
                Node left = root.left;
                root.left = root = null;
                size--;
                return left;
            }
            // 这个待删除的结点左子树和右子树都不为空,Hibbard Deletion
            // 找到当然root的后继结点,右子树的最小值结点
            Node successor = min(root.right);
            // 1.先在右子树中删除这个点
            successor.right = removeMin(root.right);
            // 2.连接root.left
            successor.left = root.left;
            //gc
            root.left = root.right = root = null;
            // 3.返回successor
            return successor;
        }
    }
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瘦皮猴117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值