题目
给定一个二叉搜索树的根节点 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 <= 10^5
节点值唯一
root 是合法的二叉搜索树
-105 <= key <= 10^5进阶: 要求算法时间复杂度为 O(h),h 为树的高度。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/delete-node-in-a-bst
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
二叉搜索树的基本操作,删除比较复杂。针对要删除节点的子节点个数的不同,需要分三种情况。
第一种情况是,如果要删除的节点没有子节点,只需要直接将父节点中,指向要删除节点的指针置为 null 。
第二种情况是,如果要删除的节点只有一个子节点(只有左子节点或者右子节点),只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。
第三种情况是,如果要删除的节点有两个子节点。需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节点,所以,我们可以应用上面两条规则来删除这个最小节点。
代码
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
// p指向要删除的节点,初始化指向根节点
TreeNode p = root;
// pp记录的是p的父节点
TreeNode pp = null;
while (p != null) {
if (p.val > key) {
pp = p;
p = p.left;
} else if (p.val < key) {
pp = p;
p = p.right;
} else {
// 进入这里就说明找到了要删除的节点
// 要删除的节点有两个子节点,查找右子树中最小节点
if (p.left != null && p.right != null) {
// 右子树中最小节点
TreeNode q = p.right;
// q的父节点
TreeNode qq = p;
while (q.left != null) {
qq = q;
q = q.left;
}
// 将最小节点的值赋给要删除位置的节点中
p.val = q.val;
// 更改p和pp指向,转为删除q,就可以通用下面的逻辑了
p = q;
pp = qq;
}
// 删除节点是叶子节点或者仅有一个子节点
TreeNode child = null;
if (p.left != null) {
child = p.left;
} else {
child = p.right;
}
// 注意这里,pp可能为null,说明删除的是根节点
if (pp == null) {
root = child;
} else if (pp.left == p) { // 删除的节点p是其父节点pp的左孩子,将p的孩子挂到pp上,下面right同理
pp.left = child;
} else {
pp.right = child;
}
// 这里要返回,不然可能无法结束循环
return root;
}
}
// 没有找到指定的删除节点
return root;
}
}