二叉搜索树的增、删、查

一、 什么是二叉搜索树?

二叉搜索树又称二叉排序树或是二叉查找树,关于它的定义:

若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

二、二叉搜索树查找value为key节点

二叉搜索树的查找是比较方便的,每次都能排除一半的元素。看图:
在这里插入图片描述
查找value为70的节点:
1、定义Node cur = root,让cur来遍历二叉树;
2、cur.val = 50 < 70,cur —> 右走,即cur = cur.right;
3、cur.val = 60 < 70,cur —> 右走,即cur = cur.right;
4、cur.val = 80 > 70,cur —> 左走,即cur = cur.left;
5、cur.val = 70 == 70,return true;
6、如果找的value为75,这个时候cur == null,不进入循环,说明没找到。return flase;

我们来代码实现一下:

public boolean findKey(int key) {
    if (root == null) return false;
    Node cur = root;
    while (cur != null) {
        if (cur.val == key){
            return true;
        }else if (cur.val < key) {
            cur = cur.left;
        }else {
            cur = cur.right;
        }
    }
    return false;
}

三、二叉搜索树增加value为key的节点

在这里插入图片描述
假如我们要增加value为25的节点:
在这里插入图片描述
增加后的效果应该是这样的,那该怎么增加呢?
我们任然时按照查找value为key的节点的思路来增加节点的:
1、先定义一个value为key = 25的新节点,即Node node = new Node(25);
2、如果当前树为空,将根节点root = node;
3、如果当前树不为空:

  1. 定义cur 遍历二叉树,定义 prev 为 cur 的父亲节点,即
    Node cur = root;
    Node prev= null;

  2. cur.val = 50 > 25,cur —> 左走,即cur = cur.left;
    cur.val = 40 > 25,cur —> 左走,即cur = cur.left;
    cur.val = 20 < 25,cur —> 右走,即cur = cur.right;
    cur.val = 30 > 25,cur —> 左走,即cur = cur.left;
    cur == null,退出循环。

  3. 判断prev.val = 30 > 25,所以25应该放在prev的左边,
    即 prev.left = node;

  4. 当插入的value 与树中的某个值相等时,直接return,二叉搜索树中的值都是不相等的。
    代码实现:

public void insertKey(int key) {
        Node node = new Node(key);
        if (root == null) {
            root = node;
            return;
        }
        Node prev = null;
        Node cur = root;

        while (cur != null) {
            if (key > cur.val) {
                prev = cur;
                cur = cur.right;
            }else if (key < cur.val){
                prev = cur;
                cur = cur.left;
            }else {
                System.out.println("插入失败,已经存在了该value的节点");
                return;
            }
        }
        if (prev.val > key) {
            prev.left = node;
        }else {
            prev.right = node;
        }
    }

四、二叉搜索树删除value为key的节点(替罪羊删除法)

同理,我们依然需要依靠二叉树的查找方法来找到需要删除的节点:

    public void removeNode(int key) {
        if (root == null) {
            return;
        }
        Node cur = root;
        Node prev = null;
        while (cur != null) {
            if (cur.val == key) {
                remove(prev,cur);
                //删除元素
                return;
            }else if(cur.val > key){
                prev = cur;
                cur = cur.left;
            }else {
                prev = cur;
                cur = cur.right;
            }
        }
    }

当我们找到需要删除的节点后,又需要分为几种情况:

  1. cur.right == null
    cur 是 root,则 root = cur.left
    cur 不是 root,cur 是 prev.left,则 prev.left = cur.left
    cur 不是 root,cur 是 prev.right,则 prev.right = cur.left

  2. cur.left == null
    cur 是 root,则 root = cur.right
    cur 不是 root,cur 是 prev.left,则 prev.left = cur.right
    cur 不是 root,cur 是 prev.right,则 prev.right = cur.right

  3. cur.left != null && cur.right != null

现在我先用cur.right == null 来举例,先看图:
在这里插入图片描述

  1. 当我们需要删除value = 50节点时:
    root.left == cur.left;

  2. 当我们需要删除value为40的节点时:
    在这里插入图片描述

cur.right == null,且 prev.left == cur;
所以我们可以:
prev.left == cur.left;

  1. 当我们需要删除value为35的节点时:
    在这里插入图片描述

当cur.ring == null,且 prev.right == cur;
所以我们可以:
prev.right == cur.left;

同理,当cur.left == null 时,也是一样的分析;

下面我们来理解 cur.left != null && cur.right != null的情况(需要使用替罪羊删除法):
在这里插入图片描述
当我们需要删除 value = 80 的节点:
根据二叉搜索树的定义,也就是它的特点:

若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

cur.left的value都小于cur.value,
而cur.right的value都大于cur.value。

我们可以在cur左边或者右边找一个value最接近80 的节点来进行删除,
并且在 cur 左边找的时候需要满足,找到的x.value 要 大于 cur.left 的 value;
在 cur 右边找的时候需要满足,找到的x.value 要 小于于 cur.right的 value
暂且命名这个节点为x。
我们在cur左边找这个x的时候,发现x一定在 cur.left的最右边;
在右边找这个x的时候,发现x一定在 cur.right 的最左边。

还有一种特殊情况:

当cur.right 没有最左边时在这里插入图片描述
或者cur.left没有最右边时
在这里插入图片描述

我们需要进行判断,看代码。
当cur.right 没有最左边时

			if (xp.left == x) {
                //此时相当于cur.left == null  cur == prev.left时    
                //删除cur节点  所以prev.left = cur.right
                xp.left = x.right;
            }else {
                //当 cur.right 没有左节点时
                xp.right = x.right;
            }

当cur.right 没有最左边时

            if (xp.right == x){
                cur.val = x.val;
            }else {
                //当 cur.left 没有右节点时
                xp.right = x.left;
            }

找到x之后将x.value 赋值 给 cur.value ,对 x 节点进行删除,我们以在cur.right 的最左边找x为例:

    public void remove(Node prev,Node cur) {
        if (cur.right == null) {
            if (cur == root) {
                root = cur.left;
            }else if (prev.left == cur) {
                prev.left = cur.left;
            }else {//prev.right == cur
                prev.right = cur.left;
            }
        }else if (cur.left == null) {
            if (cur == root) {
                root = cur.right;
            }else if (prev.right == cur) {
                prev.right = cur.right;
            }else {//prev.left == cur
                prev.left = cur.right;
            }
        }else {
            //cur左右都不为空时,
            //cur右边的下一个节点的左边找最小值
            Node xp = cur;
            Node x = xp.right;
            //在cur 右边找最小值 覆盖cur的值
            while (x.left != null) {
                xp = x;
                x = x.left;
            }
            cur.val = t.val;
            if (xp.left == x) {
                //此时相当于cur.left == null  cur == prev.left时    删除cur节点  所以prev.left = cur.right
                xp.left = x.right;
            }else {
                //当 cur.right 没有左节点时
                xp.right = x.right;
            }
            /*
            //cur左边的下一个节点的最右边找最大值
            Node xp = cur;
            Node x = cur.left;
            while (x.right != null) {
                xp = x;
                x = x.right;
            }
            if (xp.right == x){
                cur.val = x.val;
            }else {
                //当 cur.left 没有右节点时
                xp.right = x.left;
            }*/
        }
    }

五、总结

二叉搜索树的增、删、查都是围绕着查来进行的,需要修改节点(增加,删除)时就需要定义前驱。
不像链表,在修改节点时前驱后驱都可以定义。因为二叉搜索树中 cur 连自己下一步要去哪里都不知道,后驱是没法定义的。

老规矩,小白的理解,如有错误请多多指正。感谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值