leetcode 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 为树的高度。
Related Topics
树
二叉搜索树
二叉树
思路1:
删除二叉搜索树中的一个节点,分为以下几种情况(P是被删节点,P左孩子,P右孩子):
-
-
P是叶子节点,直接删除。
-
-
-
P只有一个孩子节点:有以下2种思路:
-
直接将孩子节点放到P的位置。
-
如果左孩子存在,找到P的中序遍历前驱节点 放到P的位置,同理右孩子存在找后继。
-
-
-
P有2个孩子,有2种思路解决:
-
将P的左孩子放到P的位置,然后将P的右孩子放P中序遍历的前一个节点的右孩子。(中序遍历中,P的前一个节点右孩子一定为空) 。同理,如果把P的右孩子放到P的位置,把P的左孩子放到P中序遍历后一个节点的左孩子。 缺点:会增加树的高度。
-
将P中序遍历的前一个节点或者后一个节点删除,放到P的位置。
-
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null){
return null;
}
if(root.val < key){//key在右子树中
root.right = deleteNode(root.right,key);
}else if(root.val > key){ //key在左子树中
root.left = deleteNode(root.left,key);
}else{ //root == key
//如果是叶子 节点 直接删除 返回null
if(root.left == null && root.right == null){
root = null;
}else if(root.left!=null && root.right == null){
root = root.left;
}else if(root.left == null && root.right != null){
root = root.right;
}else{
//把后继的值放到当前节点
//注意后继节点还在树中 但是这个后继节点 只有一个孩子或者是叶子 继续递归删除他
root.val = successor(root);
root.right = deleteNode(root.right,root.val);
}
}
return root;
}
//找到root中序遍历后继节点的值
public int successor(TreeNode root){
TreeNode p = root.right;
while(p.left!=null){
p = p.left;
}
return p.val;
}
}
解答成功:
执行耗时:0 ms,击败了100.00% 的Java用户
内存消耗:41.8 MB,击败了11.69% 的Java用户
当被删节点有2个孩子,用中序前驱替换。
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null){
return null;
}
if(root.val < key){//key在右子树中
root.right = deleteNode(root.right,key);
}else if(root.val > key){ //key在左子树中
root.left = deleteNode(root.left,key);
}else{ //root == key
//如果是叶子 节点 直接删除 返回null
if(root.left == null && root.right == null){
root = null;
}else if(root.left!=null && root.right == null){
root = root.left;
}else if(root.left == null && root.right != null){
root = root.right;
}else{
//用前驱节点替换
root.val = predecessor(root);
root.left = deleteNode(root.left,root.val);
}
}
return root;
}
//找到root中序遍历后继节点的值
public int successor(TreeNode root){
TreeNode p = root.right;
while(p.left!=null){
p = p.left;
}
return p.val;
}
//中序前驱节点的值
public int predecessor(TreeNode root){
TreeNode p = root.left;
while(p.right != null){
p = p.right;
}
return p.val;
}
}
可将第2种情况和第三种放在一起解决,如果右孩子存在,直接找到P中序遍历的后继放到P的位置,如果左孩子存在,直接找到中序前驱。
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null){
return null;
}
if(root.val < key){//key在右子树中
root.right = deleteNode(root.right,key);
}else if(root.val > key){ //key在左子树中
root.left = deleteNode(root.left,key);
}else{ //root == key
//如果是叶子 节点 直接删除 返回null
if(root.left == null && root.right == null){
root = null;
}else if(root.left!=null){//左孩子不为空 直接找到前驱替换
root.val = predecessor(root);
root.left = deleteNode(root.left,root.val);
}else{//root.right != null //用后继节点替换
root.val = successor(root);
root.right = deleteNode(root.right,root.val);
}
}
return root;
}
//找到root中序遍历后继节点的值
public int successor(TreeNode root){
TreeNode p = root.right;
while(p.left!=null){
p = p.left;
}
return p.val;
}
//中序前驱节点的值
public int predecessor(TreeNode root){
TreeNode p = root.left;
while(p.right != null){
p = p.right;
}
return p.val;
}
}
本文探讨如何在二叉搜索树中高效删除指定节点,同时保持树的搜索特性。讨论了删除策略,包括直接替换、中序遍历前后继节点替换,并提供了三种解决方案的代码实现。

被折叠的 条评论
为什么被折叠?



