450.删除二叉搜索树中的节点
题干描述
给定一个二叉搜索树的根节点root和一个值key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点; 如果找到了,删除它。 说明: 要求算法时间复杂度为 O ( h ) O(h) O(h),h 为树的高度。
示例:
解题思路
递归
1. 确定递归函数参数以及返回值
说到递归函数的返回值,在二叉树:搜索树中的插入操作中通过递归返回值来加入新节点, 这里也可以通过递归返回值删除节点。
2. 确定终止条件
遇到空返回,其实这也说明没找到删除的节点,遍历到空节点直接返回了。
3. 确定单层递归的逻辑
这里就把二叉搜索树中删除节点遇到的情况都搞清楚。
有以下五种情况:
- 第一种情况:没找到删除的节点,遍历到空节点直接返回了。
- 找到删除的节点
1)第二种情况:左右孩子都为空(叶子节点),直接删除节点,返回NULL为根节点。
2)第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点。
3)第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点。
4)第五种情况:左右孩子节点都不为空,则将删除节点的左子树头节点(左孩子)放到删除节点的右子树,则将删除节点右孩子为新的根节点。
普通二叉树的删除方式
这里我在介绍一种通用的删除,普通二叉树的删除方式(没有使用搜索树的特性,遍历整棵树),用交换值的操作来删除目标节点。
代码中目标节点(要删除的节点)被操作了两次:
- 第一次是和目标节点的右子树最左面节点交换。
- 第二次直接被NULL覆盖了。
迭代法
删除节点的迭代法还是复杂一些的,但其本质和递归法里介绍的一样,最关键就是删除节点的操作。
总结
大家会发现二叉搜索树删除节点比增加节点复杂的多。
因为二叉搜索树添加节点只需要在叶子上添加就可以的,不涉及到结构的调整,而删除节点操作涉及到结构的调整。
这里我们依然使用递归函数的返回值来完成把节点从二叉树中移除的操作。
这里最关键的逻辑就是第五种情况(删除一个左右孩子都不为空的节点),这种情况一定要想清楚。
递归中的两种写法,推荐大家学会第一种(利用搜索树的特性)就可以了,第二种递归写法其实是比较绕的。
最后也给出了相应的迭代法,就是模拟递归法中的逻辑来删除节点,但需要一个pre记录cur的父节点,方便做删除操作。
迭代法其实不太容易写出来,所以如果是初学者的话,彻底掌握第一种递归写法就够了。
代码实现
// 450.删除二叉搜索树中的节点
public TreeNode deleteNode(TreeNode root, int key){
if(root == null) return root;
if(root.val == key){
if(root.left == null){
return root.right;
} else if(root.right == null){
return root.left;
} else{
TreeNode cur = root.right;
while(cur.left != null){
cur = cur.left;
}
cur.left = root.left;
root = root.right;
return root;
}
}
if(root.val > key) root.left = deleteNode(root.left, key);
if(root.val < key) root.right = deleteNode(root.right, key);
return root;
}
参考资料:450.删除二叉搜索树中的节点