题目重点:从 Oracle VO 的删除目标叶节点题出发,用后序遍历一次处理孩子删除后新形成的目标叶。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 二叉树节点带 value、left、right;输入 target 是目标值,可修改树指针并返回新根。
- 只删除 value==target 且当前无孩子的节点;如果孩子删完使父节点成为目标叶,也继续删除。若面试官仅要求原始叶一次性删除,需改变口径。
解题路线
- 递归先处理左子树与右子树,并把可能为空的新根写回当前节点;这保证检查当前节点时,两侧孩子已经按规则处理。
- 若当前左右都为空且值为 target,返回 None;否则返回当前节点。根节点本身被删除时整个返回值为 None。
- 正确性按树高归纳:叶节点判断直接正确;假设两个更矮子树处理正确,父节点看到的孩子状态就是所有后续删除的最终状态,因此可准确决定是否删除。
- 迭代版本可用显式后序栈以避免极深树递归栈溢出,但要记录父指针和访问阶段。
手动走一遍
根 1,左孩子 2,左孩子的左孩子 2:最下层 2 先删;上一层 2 变成叶后也删;根 1 保留。若根值也是 2,整棵链都会删除。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
"""Delete target-valued leaves, including parents exposed by earlier deletion."""
class TreeNode:
def __init__(
self,
value: int,
left: "TreeNode | None" = None,
right: "TreeNode | None" = None,
):
self.value = value
self.left = left
self.right = right
def remove_target_leaves(root: TreeNode | None, target: int) -> TreeNode | None:
if root is None:
return None
root.left = remove_target_leaves(root.left, target)
root.right = remove_target_leaves(root.right, target)
if root.left is None and root.right is None and root.value == target:
return None
return root
复杂度
N 个节点各访问一次,O(N) 时间;递归额外栈 O(H),H 为树高,最坏 O(N),平衡树 O(log N)。原地更新指针,不计调用栈之外的额外空间。
易错点与追问
容易失分的边界
- 先检查并删除父节点再递归,漏掉删除孩子后才形成的新叶。
- 只遍历一轮原始叶,连锁层数多时结果不完整。
- 不返回新根,导致原根被删时调用者仍持有旧引用。
继续深挖
- 如果只删原始叶而不连锁,如何标记初始树中的叶并处理?
- 若树很深不允许递归,如何用显式后序遍历保证删除顺序?
