题目描述

[!green]

牛客原题: ✅ 补充题 118. 二叉树的叶子父节点剪枝

给你一棵二叉树的根节点 root,请移除操作开始前的所有叶子节点。你只能删除某个原始叶子的父节点,删除时该父节点的整棵子树都会被移除。

返回能够保留最多节点的结果树。新产生的叶子节点不需要继续删除。

如果树只有一个节点,约定返回空树;如果输入为空树,仍返回空树。

示例 1:

输入: root = [1,2,3,4,5,6,7]
输出: [1]
解释: 原始叶子为 4、5、6、7,必须删除其父节点 2、3,根 1 保留。

示例 2:

输入: root = [1,2,3,null,null,4]
输出: []
解释: 2 是原始叶子,所以其父节点 1 必须整棵删除,无法单独保留右侧分支。

示例 3:

输入: root = [1]
输出: []
解释: 采用单节点直接删除的边界约定。

示例 4:

输入: 链表状二叉树 1→2→3→4,每条边均为左孩子边
输出: 1→2
解释: 原始叶子只有 4,删除其父节点 3 的子树即可。2 虽然变成新叶子,也应保留。

提示:

  • 采用节点数不超过 1000 的递归实现版本。
  • “原始叶子”以操作前的树为准;本轮剪枝形成的新叶子保留,不级联删除。
  • 允许原地修改子指针。

题意分析

删除操作的对象是原始叶子的父节点,并连同其整棵子树一起移除。对每个原始叶子,这个父节点覆盖的子树都无法保留;所有这些必删区域之外的节点才是能够保留的部分。

因此不需要反复剪树,也不需要比较多套删除方案。只需在原始结构上识别必删子树,并保留其他节点。

解法:先判断原始叶子再剪枝

核心思路

[!blue]

进入当前节点时,先检查它是否为空、是否是叶子,或是否具有叶子孩子。空树和单节点按题面约定返回空;只要某个孩子是原始叶子,当前子树属于必删区域,直接返回空即可。

若当前节点可以保留,再递归处理左右子树,将返回的新子树根接回。必须先完成叶子判断,再修改孩子;否则子树剪枝形成的新叶子会被误认为原始叶子,导致级联删除。

必删区域中的节点没有任何合法方案可以保留,而算法保留了区域外的全部节点,所以所得树的保留节点数最大。已被整体删除的子树无需继续遍历。

解题步骤

  1. 在修改孩子之前检查当前节点是否是原始叶子的父亲;若是,整棵当前子树必须移除。
  2. 否则递归处理左右孩子并把结果接回。
  3. 按题目约定处理空树及单节点树。

代码实现

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int val) {
        this.val = val;
    }
}

class Solution {
    private boolean leaf(TreeNode node) {
        return node != null && node.left == null && node.right == null;
    }

    public TreeNode prune(TreeNode root) {
        if (root == null || leaf(root) || leaf(root.left) || leaf(root.right)) {
            return null;
        }

        root.left = prune(root.left);
        root.right = prune(root.right);

        return root;
    }
}
type TreeNode struct {
    Val         int
    Left, Right *TreeNode
}

func isLeaf(node *TreeNode) bool {
    return node != nil && node.Left == nil && node.Right == nil
}

func prune(root *TreeNode) *TreeNode {
    if root == nil || isLeaf(root) || isLeaf(root.Left) || isLeaf(root.Right) {
        return nil
    }
    root.Left = prune(root.Left)
    root.Right = prune(root.Right)
    return root
}

复杂度分析

  • 时间复杂度:$O(n)$。
  • 空间复杂度:递归空间 $O(h)$,n 为节点数、h 为树高。

关键点总结

[!green]

每个原始叶子的父节点都是必删点;直接剪去这些必删点覆盖的子树,保留其余节点,就是保留节点最多的方案。

易错点总结

[!yellow]

不能先递归更新孩子后再判断它们是不是叶子,否则会错误地级联删除整棵树。

相似题目

题目 难度 关联与区别
1325. 删除给定值的叶子节点 中等 原题会继续删除新产生的目标叶子,本题只依据原始叶子找父节点,剪枝条件与时机不同。
1110. 删点成林 中等 同样删除节点及重连树结构,原题保留被删节点下面的森林,本题直接移除指定父节点的整棵子树。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/2486889980
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!