要求
给定一个二叉搜索树的根节点 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
输出: []
核心代码
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
if not root:
return None
if root.val > key:
root.left = self.deleteNode(root.left,key)
elif root.val < key:
root.right = self.deleteNode(root.right,key)
else:
if not root.left or not root.right:
root = root.left if root.left else root.right
else:
temp = root.right
while temp.left:
temp = temp.left
root.val = temp.val
root.right = self.deleteNode(root.right,temp.val)
return root
重点问题
解题思路:
根据题目要求,我们肯定是要从上到下进行搜索的。其次,我们也要充分利用BST的性质,可以在每轮递归中比较key和根节点的大小,决定到左子树还是右子树里搜寻,如何进行删除? 删除节点主要有三种情况:
- 节点只存在左子树,那么我们直接用左子树代替根节点即可;
- 节点只存在右子树,同样地,我们直接用右子树代替根节点;
- 同时存在左右子树是比较复杂的情况,这是我们重点关注的情况。
这里我们优先采用右子树替换根节点的原则(当然左子树也可以)。 当我们找到需要删除的节点后,必须以右子树的最小节点(也就是右子树最左的节点)来代替被删除的节点,以下图的例子来说明:
这幅图中,如果我们删除节点3,则必须以先节点4替换节点3,再在下两轮递归中,把节点3去掉。
这样才能保持BST的性质不发生变化,满足题目要求。
实际上我们在操作的过程中是先在搜索树中进行搜索,决定要删除那个节点,然后我们看要删除节点是否是叶子节点,叶子节点直接删除即可;是否有左子树或者是否有右子树(单一存在),存在单一左右子树我们直接将剩下的拼接上去即可;假设左右子树都存在,那么我们就需要保证我们删除节点之后,仍然保持这个树是一个搜索二叉树,我们采用的是使用右子树中最左下方的点来替代要删除节点(这个删除实际上是替换),最终别忘了将替换的节点也进行删除(这个才是实际的删除),非常好的一道题。