二叉搜索树的删除操作详解,图文并茂,化繁为简


前言


一、二叉树搜索树概念

二叉搜索树又称为二叉排序树,它具有二叉树以下的性质:
-若它的左子树不为空,则左子树上所有的结点的值都小于根节点的值;
-若它的右子树不为空,则右子树上所有的结点的值都大于根节点的值;
-它的左右子树也分别为二叉搜索树。

二叉树搜索树的中序一定有序。
int[] a={5,3,7,1,4,6,8,9};
中序遍历:{1,3,4,5,6,7,8,9};

在这里插入图片描述

搜索二叉树的遍历和插入比较简单,所以在这主要讲解一下二叉搜索树的删除操作,我看老师讲解的二叉树的删除操作视频看了好几遍,还看了好几篇讲解二叉搜索树删除操作的,终于理解了,所以想用自己的方法记录一下这个学习的过程。

二、二叉树删除操作的讲解

1.分情况讨论:

删除操作可以分为3种情况:node为待删除的节点,parent为待删除结点的父节点

1.node左右孩子均不存在
2.node只有左孩子或者只有有孩子
3.node左右孩子均存在

第一种情况:当待删除结点node的左右孩子均不存在的时候,那么它是叶子结点或者只有一个根结点的树;删除的时候如果node是根节点,则让根结点为空;如果node为其父节点的左孩子,则把父亲左孩子置为空;否则把父亲右孩子置为空。(如果这一段文字看不懂,可以自己画个图试试看,相信你自己)
在这里插入图片描述
代码如下:

   if(node.left==null&&node.left==null){
    
    
            if(node==root){
    
    
                root=null;
            }else if(node==parent.left){
    
    
                parent.left=null;
            }else{
    
    
                parent.right=null;
            }
   }

第二种情况:当待删除的结点node只有左孩子或者右孩子时,那么在删除的时候要断两个联系,node和parent,node和它的孩子结点的联系;再新建一个一个联系;如下图所示:当要删除的结点没有左孩子时,就是下图中左边的这种情况:
1.首先如果待删除结点为根节点,则让待删除的节点的左孩子为根;
2.如果待删除结点为其父节点的左孩子,则让待删除的结点的左孩子指向其父节点。
3.如果待删除结点为其父节点的右孩子,则让待删除的结点的左孩子指向其父节点。

在这里插入图片描述

if(node.left!=null&&node.right==null){
    
    
            //有左孩子,没有右孩子
            if(node==root){
    
    
                root=node.left;
            }else if(node==parent.left){
    
    
                parent.left=node.left;
            }else{
    
    
                parent.right=node.left;
            }
            
        }else if(node.left==null&&node.right!=null){
    
    
        //有右孩子,没有左孩子
            if(node==root){
    
    
                root=node.right;
            }else if(node==parent.left){
    
    
                parent.left=node.right;
            }else{
    
    
                parent.right = node.right;
            }
        }

第三种情况
当待删除的结点左右孩子都有时,此时采用替换法,定义两个节点,ghost和ghostParent,ghost中存放待删除结点左子树中最大的结点(或者右子树中最小的值替换),ghostParent保存ghost的父节点,找到ghost后,用ghost的值替换待删除结点node的值,然后判断待删除结点是否有左孩子,如果有,则让其左孩子指向其父节点(ghostParent)。

在这里插入图片描述

            Node ghost=node.left;
            Node ghostParent=null;
            while(ghost.right!=null){
    
    
                ghostParent=ghost;
                ghost=ghost.right;
            }
            //进行替换
            node.key=ghost.key;
            //删除ghost结点(其右孩子一定为空)
            if(node==ghostParent){
    
    
                ghostParent.left=ghost.left;
            }else{
    
    
                ghostParent.right=ghost.left;
            }

2.完整代码

public boolean remove(Integer key){
    
    
        //找要删除的结点key所在的结点为node,node的双亲结点,记作parent
        Node current=root;
        Node parent=null;
        while(current!=null){
    
    
            int comp=key.compareTo(current.key);
            if(comp==0){
    
    
                removeInternal(current,parent);
                return true;
            }else if(comp<0){
    
    
                parent=current;
                current=current.left;
            }else{
    
    
                parent=current;
                current=current.right;
            }
        }
        return false;
    }
    private void removeInternal(Node node,Node parent){
    
    
        //总共有四种情况:1.两边都没有左右孩子 2.只有一边有孩子  3.两边都有子孩子

        //node为要删除的结点
        if(node.left==null&&node.left==null){
    
    
            if(node==root){
    
    
                root=null;
            }else if(node==parent.left){
    
    
                parent.left=null;
            }else{
    
    
                parent.right=null;
            }
        }else if(node.left!=null&&node.right==null){
    
    
            //有左节点,没有右节点

            if(node==root){
    
    
                root=node.left;

            }else if(node==parent.left){
    
    
                parent.left=node.left;
            }else{
    
    
                parent.right=node.left;
            }
        }else if(node.left==null&&node.right!=null){
    
    
            if(node==root){
    
    
                root=node.right;
            }else if(node==parent.left){
    
    
                parent.left=node.right;
            }else{
    
    
                parent.right = node.right;
            }
        }else{
    
    
            //左右孩子都有的时候
            Node ghost=node.left;
            Node ghostParent=null;
            while(ghost.right!=null){
    
    
                ghostParent=ghost;
                ghost=ghost.right;
            }
            //进行替换
            node.key=ghost.key;
            //删除ghost结点(其有孩子一定为空)
            if(node==ghostParent){
    
    
                ghostParent.left=ghost.left;
            }else{
    
    
                ghostParent.right=ghost.left;
            }
        }
    }


总结

1.对于删除操作,其实代码不复杂,前两种情况考虑起来也比较简单,主要是第三种情况,有点复杂,采用替换法的思想,找待删除结点左子树中最大的值替换或者右子树中最小的值替换,才能保证二叉搜索树的结构完整。
2.我们老师说过,写代码其实是做题的最后一步,首先你要学会分析一个题,最好把思路能在自己的脑子中过一遍,结合画图,想明白过程之后再动手,这才是提高自己代码能力的有效途径。

猜你喜欢

转载自blog.csdn.net/m0_46551861/article/details/109324429