Medium
题目描述
给你二叉树的根节点 root 和一个整数 limit ,请同时删除树中所有 不足节点 ,并返回最终二叉树的根节点。
假如通过节点 node 的每种可能的 “根-叶” 路径上值的总和全都小于给定的 limit,则该节点被称之为 不足节点 ,需要被删除。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,4,-99,-99,7,8,9,-99,-99,12,13,-99,14], limit = 1
输出:[1,2,3,4,null,null,7,8,9,null,14]
示例 2:
输入:root = [5,4,8,11,null,17,4,7,1,null,null,5,3], limit = 22
输出:[5,4,8,11,null,17,4,7,null,null,null,5]
示例 3:
输入:root = [1,2,-3,-5,null,4,null], limit = -1
输出:[1,null,-3,4]
提示:
- 树中节点数目在范围
[1, 5000]内 -10^5 <= Node.val <= 10^5-10^9 <= limit <= 10^9
解题思路
这道题需要删除所有"不足节点",即通过该节点的每条根到叶路径的和都小于 limit 的节点。
核心思路:
使用深度优先搜索(DFS)进行后序遍历,从叶子节点开始判断。关键是要理解"不足节点"的定义:一个节点是不足的,当且仅当通过它的所有根到叶路径的和都小于 limit。
算法步骤:
- 对于每个节点,递归处理其左右子树
- 如果是叶子节点,直接判断当前路径和是否满足条件
- 对于非叶子节点,需要检查处理后是否还有子节点存在:
- 如果左右子树都被删除了,说明通过该节点的所有路径都不满足条件
- 如果还有子节点存在,说明至少有一条路径满足条件
判断逻辑:
- 叶子节点:当前路径和 >= limit 时保留
- 非叶子节点:递归处理后至少有一个子节点存在时保留
这种方法确保了我们正确识别并删除所有不足节点,同时保留所有有效路径上的节点。
推荐解法: 后序遍历 DFS,时间复杂度 O(n),空间复杂度 O(h)。
代码实现
class Solution {
public:
TreeNode* sufficientSubset(TreeNode* root, int limit) {
if (!root) return nullptr;
// 如果是叶子节点,判断当前路径和是否满足条件
if (!root->left && !root->right) {
return root->val >= limit ? root : nullptr;
}
// 递归处理左右子树
if (root->left) {
root->left = sufficientSubset(root->left, limit - root->val);
}
if (root->right) {
root->right = sufficientSubset(root->right, limit - root->val);
}
// 如果左右子树都被删除了,说明通过当前节点的所有路径都不满足条件
if (!root->left && !root->right) {
return nullptr;
}
return root;
}
};
class Solution:
def sufficientSubset(self, root: Optional[TreeNode], limit: int) -> Optional[TreeNode]:
if not root:
return None
# 如果是叶子节点,判断当前路径和是否满足条件
if not root.left and not root.right:
return root if root.val >= limit else None
# 递归处理左右子树
if root.left:
root.left = self.sufficientSubset(root.left, limit - root.val)
if root.right:
root.right = self.sufficientSubset(root.right, limit - root.val)
# 如果左右子树都被删除了,说明通过当前节点的所有路径都不满足条件
if not root.left and not root.right:
return None
return root
public class Solution {
public TreeNode SufficientSubset(TreeNode root, int limit) {
if (root == null) return null;
// 如果是叶子节点,判断当前路径和是否满足条件
if (root.left == null && root.right == null) {
return root.val >= limit ? root : null;
}
// 递归处理左右子树
if (root.left != null) {
root.left = SufficientSubset(root.left, limit - root.val);
}
if (root.right != null) {
root.right = SufficientSubset(root.right, limit - root.val);
}
// 如果左右子树都被删除了,说明通过当前节点的所有路径都不满足条件
if (root.left == null && root.right == null) {
return null;
}
return root;
}
}
var sufficientSubset = function(root, limit) {
if (!root) return null;
// 如果是叶子节点,判断当前路径和是否满足条件
if (!root.left && !root.right) {
return root.val >= limit ? root : null;
}
// 递归处理左右子树
if (root.left) {
root.left = sufficientSubset(root.left, limit - root.val);
}
if (root.right) {
root.right = sufficientSubset(root.right, limit - root.val);
}
// 如果左右子树都被删除了,说明通过当前节点的所有路径都不满足条件
if (!root.left && !root.right) {
return null;
}
return root;
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 需要访问树中的每个节点一次 |
| 空间复杂度 | O(h) | 递归调用栈的深度,h 为树的高度,最坏情况下为 O(n) |