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

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

一般来说,删除节点可分为两个步骤:

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

 

示例 1:

输入:root = [5,3,6,2,4,null,7], key = 3
输出:[5,4,6,2,null,null,7]
解释:给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
另一个正确答案是 [5,2,6,null,4,null,7]。

示例 2:

输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]
解释: 二叉树不包含值为 0 的节点

示例 3:

输入: root = [], key = 0
输出: []

提示:

  • 节点数的范围 [0, 104].
  • -105 <= Node.val <= 105
  • 节点值唯一
  • root 是合法的二叉搜索树
  • -105 <= key <= 105

进阶: 要求算法时间复杂度为 O(h),h 为树的高度。

题解

递归

通过递归不断更新节点的子树,而二叉搜索树的删除 可以查看这里。递归的时、空复杂度都是O(n),n为节点数,分别是因为最坏要遍历所有节点和

递归
class Solution {
public:
    
    TreeNode* deleteNode(TreeNode* root, int key) {
        if(root==nullptr)
            return root;
        if(key<root->val){//小于往左走
            root->left=deleteNode(root->left,key);//递归修改左子树
            return root;
        }
        if(key>root->val){//大于往右走
            root->right=deleteNode(root->right,key);
            return root;
        }

        //接下来是等于的情况
        if(root->left==nullptr&&root->right==nullptr)//没有子树
        {
            return nullptr;
        }
        if(root->left==nullptr)//没有左子树,用右子树替换当前节点
        {
            return root->right;
        }
        if(root->right==nullptr)//反过来
        {
            return root->left;
        }
        //两个子树都存在,寻找后继节点,左子树最大/右子树最小的节点
        TreeNode* after=root->left;
        while(after->right!=nullptr){//找到左子树最大节点,一直往右走
            after=after->right;
        }

        //修改节点
       
        //1.原左子树修改:删除后继节点
        root->left=deleteNode(root->left,after->val);
        //2.后继节点变成新的根节点
        after->left=root->left;
        after->right=root->right;
        return after;
    }
};

迭代

 之前这个题:力扣 235. 二叉搜索树的最近公共祖先有通过迭代遍历二叉树的树的例子,可以节约递归的空间开销。

没有递归那么好理解,要多画图来体会。

省掉了递归,空间复杂度O(1)。

时间复杂度:寻找被删节点+寻找前驱/后继,都与高度相关,从顶向下访问一次,为O(H),即O(logn),最坏为O(n),遍历所有节点

迭代
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        TreeNode* cur=root;//迭代节点
        TreeNode* curParent=nullptr;//cur的父节点
        while(cur!=nullptr&&cur->val!=key){//迭代寻找节点
            curParent=cur;
            cur=cur->val>key?cur->left:cur->right;
        }
        if(cur==nullptr)//没找到
            return root;
        if(cur->left==nullptr&&cur->right==nullptr)//没有子树,直接删除
            cur=nullptr;
        else if(cur->left==nullptr){//没有左子树,右节点替代cur
            cur=cur->right;
        }
        else if(cur->right==nullptr){//反过来
            cur=cur->left;
        }
        else{
             //子树都存在
            TreeNode* successor=cur->right;//选右子树最小节点作为后继节点,或者左子树找前驱也可以
            TreeNode* successorParent=cur;//后继节点的父节点
            while(successor->left!=nullptr){
                successorParent=successor;
                successor=successor->left;
            }
            if(successorParent->val==cur->val){//cur右子树只有一个节点
                successorParent->right=successor->right;//4的右子树,变成3的
            }
            else{
                successorParent->left=successor->right;//如果4下面有3.5和4.5,new=3.5,newP=3,3.5还有右子树3.6,连接4-3.6
            }
            
            //后继节点继承cur的子树
            successor->right=cur->right;
            successor->left=cur->left;
            cur=successor;//用后继节点替换cur
        }
        if(curParent==nullptr)//cur是根节点
            return cur;
        //不是根节点
        if(curParent->left&&curParent->left->val==key)//cur是父节点的左子树
            curParent->left=cur;
        else//左
            curParent->right=cur;
        return root;
    }
};

 

posted @ 2022-11-30 22:26  付玬熙  阅读(18)  评论(0编辑  收藏  举报