刷题记录(Day 10)

二叉搜索树的删除

LeetCode-450. 删除二叉搜索树中的节点

题目描述:

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。

关于二叉搜索树,在本文下面有相关的介绍,只要是满足左子树节点小于根节点,右子树节点大于根节点的一颗二叉树就是二叉搜索树(BST)。

解题思路:

具体来说应该有三种思路
1、递归法
2、迭代法(本题采用)
3、通用二叉树覆盖删除法

迭代法删除二叉搜索树节点代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        TreeNode *cur = root;
        TreeNode * parent = NULL;
        TreeNode * right;
        while(cur){
            if(cur->val == key){//发现要删除的节点
                TreeNode* aftern = NULL;

                if(cur->left && cur->right){//要删除节点具有左子树和右子树,转移左子树的右子树到右子树
                    right = cur->right;//要删除节点的右根节点
                    while(right && right->left != NULL){
                        right = right->left;
                    }
                    right->left = cur->left->right;//转移要删除节点右子树
                    cur->left->right = cur->right;
                    aftern = cur->left;
                }
                else if(cur->left){
                    aftern = cur->left;
                }else if(cur->right){
                    aftern = cur->right;
                }
                if(!parent){//删除的是根节点
                    root =  aftern;
                    break;
                }else{//删除的非根节点
                    if(key < parent->val){
                        parent->left = aftern;
                    }else{
                        parent->right = aftern;
                    }
                    break;
                }
            }
            parent = cur;
            if(key < cur->val){
                cur = cur->left;
            }else{
                cur = cur->right;
            }
        }
        return root;
    }
};

二叉搜索树(BST,Binary Search Tree)

也称二叉排序树或二叉查找树。
二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:
非空左子树的所有键值小于其根结点的键值。
非空右子树的所有键值大于其根结点的键值。
左、右子树都是二叉搜索树。

二叉搜索树的例子

二叉搜索树的查找操作:
(1)查找从根结点开始,如果树为空,返回NULL
(2)若搜索树非空,则根结点关键字和X进行比较,并进行不同处理:
  ① 若X小于根结点键值,只需在左子树中继续搜索;
  ② 如果X大于根结点的键值,在右子树中进行继续搜索;
  ③若两者比较结果是相等,搜索完成,返回指向此结点的指针。

代码如下:

Position Find( ElementType X, BinTree BST )
{
    if( !BST ) return NULL; /*查找失败*/
    if( X > BST->Data )
        return Find( X, BST->Right ); /*在右子树中继续查找*/
    else if( X < BST->Data )
        return Find( X, BST->Left ); /*在左子树中继续查找*/
    else /* X == BST->Data */
        return BST; /*查找成功,返回结点的找到结点的地址*/
}

值得说明的是,上述的代码使用的是递归的方式,而使用递归会导致效率不高。恰巧这段代码又是尾递归的方式(尾递归就是程序分支的最后,也就是最后要返回的时候才出现递归),从编译的角度来讲,尾递归都可以用循环的方式去实现。由于非递归函数的执行效率高,可将“尾递归”函数改为迭代函数。
迭代法代码如下:

Position IterFind( ElementType X, BinTree BST )
{
     while( BST ) 
     {
         if( X > BST->Data )
             BST = BST->Right; /*向右子树中移动,继续查找*/
         else if( X < BST->Data )
             BST = BST->Left; /*向左子树中移动,继续查找*/
         else /* X == BST->Data */
             return BST; /*查找成功,返回结点的找到结点的地址*/
     }
     return NULL; /*查找失败*/
}

最后要说的是,查找的效率决定于树的高度。
2. 查找最大和最小元素
首先说明的是:
①最大元素一定是在树的最右分枝的端结点上。
②最小元素一定是在树的最左分枝的端结点上。
根据上述两点,我们可以很轻松的把代码写出来:

Position FindMin( BinTree BST )
{
     if( !BST ) 
         return NULL; /*空的二叉搜索树,返回NULL*/
     else if( !BST->Left )
         return BST; /*找到最左叶结点并返回*/
     else
         return FindMin( BST->Left ); /*沿左分支继续查找*/
}

上面是查找最小元素的递归函数,由于这也是个尾递归,所以我们依旧可以把它改成迭代函数,代码如下:

Position FindMax( BinTree BST )
{
     if(BST )
         while( BST->Right )  /*沿右分支继续查找,直到最右叶结点*/
             BST = BST->Right;
     return BST;
} 
  1. 二叉搜索树的插入
    二叉搜索树在插入前,肯定要找到插入的位置。所以解决这个问题的关键就是要找到元素应该插入的位置。
  2. 二叉搜索树的删除
    相对插入而言比较麻烦,需要重构一下二叉搜索树,具体看最上方代码。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值