给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
示例:
root = [5,3,6,2,4,null,7]
key = 3
5
/ \
3 6
/ \ \
2 4 7
给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
5
/ \
4 6
/ \
2 7
另一个正确答案是 [5,2,6,null,4,null,7]。
5
/ \
2 6
\ \
4 7
题目就是简单的删除 二叉搜索树中的一个节点而已。
一、迭代法
分情况考虑的话,思路清晰又简单:
- 待删除节点无子节点时:
那就直接将 指向待删除节点的指针,置空即可 - 待删除节点有一个子节点时:
将 指向待删除节点的指针 指向 待删除节点的儿子(需要判断是左儿子还是右儿子) - 待删除节点有两个子节点:
找到待删除节点右子树中的最小值,来替换掉待删除节点。
我们知道,想要删除某个节点,肯定要先找到这个节点,迭代法去找的话,那一定要用两个指针的,一个用来正在查找的位置,另一个记录此位置的父节点(因为删除节点时,需要知道此节点的父节点嘛)
所以java代码如下:
(1)查找待删除的点:
TreeNode p=root;//利用p去遍历,保留root不动,最后得返回root
TreeNode pp=null;//p的父节点
while(p!=null&&p.val!=key){
pp=p;
if(key<p.val) p=p.left;
else p=p.right;
}//此循环结束后,p就是待删除的节点,pp是p的父节点
if(p==null) return root;//没有找到待删除的值
(2)待删除节点无子节点
//如果p没有孩子,则直接把pp指向置空
if(p.left==null&&p.right==null) {
if(pp.left==p) pp.left=null;
if(pp.right==p) pp.right=null;
}
(3)待删除节点有一个子节点时:
//如果p只有一个孩子,那直接把孩子换上来即可
TreeNode child=null;
if(p.left==null&&p.right!=null) child=p.right;
if(p.left!=null&&p.right==null) child=p.left;
if(pp.left==p) pp.left=child;
if(pp.right==p) pp.right=child;
(4)待删除节点有两个子节点:
//如果p有左右子树,那就将其右子树的最小值来替换p
if(p.left!=null&&p.right!=null){
TreeNode rp=p.right;
TreeNode rpp=p;
while(rp.left!=null){
rpp=rp;
rp=rp.left;
}//循环结束后,rp就是要找的右子树的最小值,rpp是其父节点
//将rpp指向rp的指针置空
if(rpp.left==rp) rpp.left=null;
if(rpp.right==rp) rpp.right=null;
//将待删除节点的val换上
p.val=rp.val;
}
但是呢,像上面这样就要有很多的if去判断所出现的各种情况,非常繁琐。
发现了大神的精简的写法:
观察到上述,一定都有将指针置空和将指针重新指向的过程。
所以大神的方法是将这两个步骤合起来,让三种情况共用一组指针置空和重新指向的过程。
java代码如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
TreeNode p=root;//利用p去遍历,保留root不动,最后得返回root
TreeNode pp=null;//p的父节点
while(p!=null&&p.val!=key){
pp=p;
if(key<p.val) p=p.left;
else p=p.right;
}//此循环结束后,p就是待删除的节点,pp是p的父节点
if(p==null) return root;//没有找到待删除的值
//如果p有左右子树,那就将其右子树的最小值来替换p
if(p.left!=null&&p.right!=null){
TreeNode rp=p.right;
TreeNode rpp=p;
while(rp.left!=null){
rpp=rp;
rp=rp.left;
}//循环结束后,rp就是要找的右子树的最小值,rpp是其父节点
//将待删除节点的val换上
p.val=rp.val;
//将rpp指向rp的指针置空
//if(rpp.left==rp) rpp.left=null;
//if(rpp.right==rp) rpp.right=null;
p=rp;
pp=rpp;
}
//!!!此方法的这里不好理解!!!
//删除节点是叶子节点或仅有一个子节点
TreeNode child;//p的子节点
if(p.left!=null) child=p.left;
else if(p.right!=null) child=p.right;
else child=null;
if(pp==null) root=child;//删除的是根节点
else if(pp.left==p) pp.left=child;
else pp.right=child;
return root;
}
}
二、递归法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root==null) return null;
if(key<root.val){
root.left=deleteNode(root.left,key);
}else if(key>root.val){
root.right=deleteNode(root.right,key);
}else{
//root节点正是待删除的值
if(root.left==null){
//当前节点没有左子树
return root.right;
}else if(root.right==null){
return root.left;
}else{
//当前节点有左右子树,将左子树直接放到右子树的最小节点左侧即可
//找右子树的最小节点
TreeNode minP=root.right;
while(minP.left!=null){
minP=minP.left;
}//minP即为右子树的最小节点
minP.left=root.left;
return root.right;
}
}
return root;
}
}