450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
示例 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;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步