(二叉树)二叉搜索树的查找、插入和删除

1.二叉搜索树简介

二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉搜索树。二叉搜索树的特点之一即是其中序遍历为升序。

2.二叉搜索树的查找

根据二叉搜索树的性质,每个结点的值都是大于其左子结点值(左子结点非空)并且小于其右子结点值(右子结点非空),因此可以考虑二分查找的方法:比较目标值与当前结点值,如果目标值大于当前结点值,那么说明目标值不可能存在于当前结点的左子树中,应当再从其右子树中去查找;如果目标值小于当前结点值,那么说明目标值不可能存在于当前结点的右子树中,应当再从其左子树中去查找;如果目标值等于当前结点值,就返回当前结点,因此就不难写出代码了:

TreeNode* searchBST(TreeNode* root, int val) {
        if(!root)return NULL;   //如果到了空结点依然没找到,说明不存在
        
        if(root->val==val)return root;   //目标值等于当前结点值则返回当前结点
        if(root->val>val)    //目标值小于当前结点值,就从左子树中去查找
            return searchBST(root->left,val);
        
        return searchBST(root->right,val);   //目标值大于当前结点值,就从右子树中去查找

3.二叉搜索树的插入

要想将结点插入到二叉搜索树中,那么首先就需要找到插入的位置,那插入的位置怎么找呢?实际上,将结点插入到二叉搜索树中的方法并不唯一,举个例子:
向下面二叉搜索树中插入结点2:
在这里插入图片描述
插入的结果并不唯一:
在这里插入图片描述
在这里插入图片描述
当然不止这两种插入方式,这里介绍一种比较简单的方法,具体的方法如下:如二叉查找,比较待插入结点值(目标值)与当前结点值的大小,如果目标值大于当前结点值,那么说明应当将目标结点插入到右子树中;如果目标值小于当前结点值,那么说明应当将目标结点插入到左子树中,如果搜索到空结点,说明目标结点就应该插入这个空结点的位置了,这种方法实现结果就如上面第二种插入方式,代码如下:

TreeNode* insertIntoBST(TreeNode* root, int val) {
        
        if(!root)    //如果当前结点值为空,那么说明应当将目标结点插入到该空结点位置,就直接新建
        {
            TreeNode* newNode=new TreeNode(val);
            return newNode;
        }
        
        if(root->val==val)return root;   //如果目标结点值与当前结点值相同,就直接返回当前结点
        
        if(root->val<val)  //如果目标结点值大于当前结点值,就在右子树中进行查找
            root->right=insertIntoBST(root->right,val);
        
        else root->left=insertIntoBST(root->left,val);//如果目标结点值小于当前结点值,就在左子树中进行查找
        
        return root;   //返回结点
    }

4.二叉搜索树的删除

二叉搜索树的删除是比较麻烦的,删除结点还是需要先找到待删除结点的位置,找到待删除结点实际上就是二叉搜索树的查找了,这里就不多说了,主要说一下找到待删除结点后该怎么操作。这里一共有以下几种情况:
①待删除结点没有子结点;
②待删除结点左子树为空,右子树非空;
③待删除结点右子树为空,左子树非空;
④待删除结点左右子树皆非空。
第①种情况比较简单,直接返回空结点即可,现在来说下其他情况:
对于第②种情况,举个例子:删除下面二叉搜索树中的1
在这里插入图片描述
那么删除后的结果就应该将2结点移到1结点,如下:
在这里插入图片描述
由此也可总结出第②种情况的删除方法:从当前结点的右子结点开始,一直遍历左子结点,来在当前结点的右子树中找到最小值结点,然后将最小值赋值给当前结点值,再删除最小值结点。

需要注意的是,最小值结点可能是叶子结点,也可能还有右子树,如果是叶子节点,那么直接让该最小值结点的双亲结点的左子树指针指向NULL即可;

如果最小值结点还有右子树的话,如在上图中继续删除3结点,就还需要再处理3结点的右子树,那么就只需要将最小值结点的右子树接在最小值结点双亲结点的左子树上即可,换句话说,就是将图中的结点4接在结点3的双亲结点5的左子树上。

除此之外,还有一种特殊情况,即是当前结点的右子结点就是最小值结点了,那么说明其右子结点的左子树必定为空了,此时只需将其右子结点的值赋值给当前结点,并将其右子结点的右子树接到当前结点的右子树上即可。

实现代码如下:

                //在右子树中找到最小结点值来替换当前根结点值
                TreeNode* rightMin=root->right;
                TreeNode* lastMin=root;   //保存双亲结点
                while(rightMin->left)
                {
                    lastMin=rightMin;   //保存上一个结点
                    rightMin=rightMin->left;  //最小结点值必定是在根结点右子结点的左子树中找
                }
                
                root->val=rightMin->val;   //将找到的最小结点值赋值给根结点
                
                if(lastMin==root)lastMin->right=rightMin->right;  //如果根结点的右子结点就是右子树中最小的了,即右子结点
                                                          的左子结点为空,就直接将右子结点的右子树接到根结点右子树上即可
                else lastMin->left=rightMin->right;   //否则就将最小值结点的右子树接到最小结点的上一个结点的左子树上

第③种情况,左子树非空,右子树为空,删除的方法与上述类似,就不多说了;
第④种情况,对于左右子树均非空的情况,其实就既可以从左子树中找到最大结点来取代当前结点,也可以从右子树中找到最小结点来取代当前结点,两种方法都是可以的。

综合以上,代码如下:

TreeNode* deleteNode(TreeNode* root, int key) {
        
        if(!root)return NULL;
        
        if(root->val==key)   //根结点即是要删除的
        {
            if(!root->left&&!root->right)//左右皆空
                return NULL;
            
            else if(!root->left&&root->right)   //左子树空,右子树非空
            {
                //在右子树中找到最小结点值来替换当前根结点值
                TreeNode* rightMin=root->right;
                TreeNode* lastMin=root;
                while(rightMin->left)
                {
                    lastMin=rightMin;   //保存上一个结点
                    rightMin=rightMin->left;  //最小结点值必定是在根结点右子结点的左子树中找
                }
                
                root->val=rightMin->val;   //将找到的最小结点值赋值给根结点
                
                if(lastMin==root)lastMin->right=rightMin->right;  //如果根结点的右子结点就是右子树中最小的了,即右子结点的左子结点为空,就直接将右子结点的右子树接到根结点右子树上即可
                else lastMin->left=rightMin->right;   //否则就将最小值结点的右子树接到最小结点的上一个结点的左子树上
                
            }
            else    //左子树非空,右子树空  或者 左右子树均非空   均非空的情况下即可以在左子树中找最大的也可以在右子树中找最小的
            {
                //在左子树中找到最大结点值来替换当前根结点值
                TreeNode* leftMax=root->left;
                TreeNode* lastMax=root;
                while(leftMax->right)
                {
                    lastMax=leftMax;   //保存上一个结点
                    leftMax=leftMax->right;  //最大结点值必定是在根结点左子结点的右子树中找
                }
                
                root->val=leftMax->val;   //将找到的最小结点值赋值给根结点
                
                if(lastMax==root)lastMax->left=leftMax->left;   //如果根结点的左子结点就是左子树中最大的了,即左子结点的右子结点为空,就直接将左子结点的左子树接到根结点左子树上即可
                else lastMax->right=leftMax->left;  //否则就将最大值结点的左子树接到最大结点的上一个结点的右子树上
            }
        }
        else if(root->val>key)
            root->left=deleteNode(root->left,key);   //如果当前结点值大于目标值,就从左子树中删除目标值
        else root->right=deleteNode(root->right,key);   //如果当前结点值小于目标值,就从右子树中删除目标值
        
        
        return root;
        
    }

猜你喜欢

转载自blog.csdn.net/qq_28114615/article/details/85844979