目录
1. 问题描述
给定一个二叉搜索树的根节点 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, 10^4]
. -10^5 <= Node.val <= 10^5
- 节点值唯一
root
是合法的二叉搜索树-10^5 <= key <= 10^5
进阶: 要求算法时间复杂度为 O(h),h 为树的高度。
2. 思路与算法
找到key对应的节点并删除很容易,但是由于删除一个节点后会破坏原来的二叉搜索树的结构,需要进行结构调整使其仍然保持二叉搜索树的性质,这个比较难。
首先,如果输入二叉树中没有值为key的节点,不必进行任何操作,返回原来的根节点即可。
以下假设树中存在值为key的节点,分情况讨论。
- root为空。没有对应于key的节点可以删除,仍然输出空的树,返回None
- root.val < key。说明key对应的节点在右子树上,针对右子树进行递归调用,仍然返回root
- root.val > key。说明key对应的节点在左子树上,针对左子树进行递归调用,仍然返回root
- root.val = key。说明需要删除root节点,需要进一步分情况考虑如何调整树的结构使其仍然满足二叉搜索树的性质
- root为叶子节点。删除root后,变成了空的树,返回None
- root只有右子树,删除root后,右子树仍然是一个二叉搜索树,root的右子节点成为新的树的根节点,因此返回root的右子节点即可
- root只有右子树,删除root后,左子树仍然是一个二叉搜索树,root的左子节点成为新的树的根节点,因此返回root的左子节点即可
- root既有左子树又有右子树。这个情况比较复杂一些。新的根节点应该是左子树中最大的节点或者右子树中最小的节点。这里考虑根节点的后继者(successor)即右子树中的最小的节点作为新的根节点。然后,原左子树仍然为左子树,原来的右子树(删除作为新的根节点的那个叶子节点后)仍然作为根节点的右子树。
3. 代码实现
# Definition for a binary tree node.
# 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 root==None:
return None
if root.val < key:
root.right = self.deleteNode(root.right, key)
elif root.val > key:
root.left = self.deleteNode(root.left, key)
else:
if root.left==None and root.right==None:
# 既没有左子树,也没有右子树
return None
elif root.left==None and root.right!=None:
# 只有右子树
return root.right
elif root.left!=None and root.right==None:
# 只有左子树
return root.left
else:
# 既有左子树,也有右子树
# 搜所根节点的successor,从右子节点出发一直沿着左子树往下寻找直到最后
# 一个没有左子节点的节点。然后递归地针对root.right调用deleteNode来删
# 除successor。因为successor 没有左子节点,因此这一步递归调用不会再
# 次步入这一种情况。然后将successor 更新为新的 root 并返回。
successor = root.right
while successor.left:
successor = successor.left
successor.right = self.deleteNode(root.right, successor.val)
successor.left = root.left
return successor
return root
执行用时:60 ms, 在所有 Python3 提交中击败了74.62%的用户
内存消耗:19.4 MB, 在所有 Python3 提交中击败了71.79%的用户
回到总目录:Leetcode每日一题总目录(动态更新。。。)