leetCode 1080.根到叶路径上的不足节点 + 递归 + 图解

给你二叉树的根节点 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. 添加一个递归参数 sumPath,表示从根到当前节点的路径和

(O_O)?疑惑:

  • ① 叶子节点什么时候能删除?
  • ② 非叶子节点若还有儿子未被删除,它能否被删除?
  • ③ 非叶子节点的儿子都被删除,意味着什么?

思路和分析:

  • ① 对于一个叶子节点(node) ,从根到这个叶子节点(leaf )的路径仅有一条,那么这条路径的元素和小于limit,就删掉该叶子节点
  • ② 对于一个非叶子节点(node) ,若 node 还有儿子未被删除,那么 node 就不能被删除
    • 反证法证明:设把 node 删除,那么经过 node 的所有路径和都小于 limit ,这意味着经过 node 的儿子路径和也是小于 limit ,那么 node 的儿子也应当被删除,矛盾!故 node不能被删除
  • ③ 对于一个非叶子节点(node )的儿子都被删除,意味着经过 node 的所有儿子的路径和都小于 limit 。这等价于经过 node 的所有路径和都小于 limit ,故 node 也应当被删除。
    • 总结:当且仅当 node 的所有儿子都被删除,才可删除非叶节点 node

算法1: 添加一个递归参数 sumPath,表示从根到当前节点的路径和

  • ① 如果当前节点是叶子节点(leaf ),且此时 sumPath < limit (说明从根到这个叶子节点的路径和小于limit),那么删除这个叶子节点
  • ② 如果当前节点是非叶子节点(node ),继续往下递归,node 的左儿子(为leaf )时且经过 node 的左儿子路径和也是小于limit ,就删除这个儿子;node 的右儿子(为leaf)时且经过 node 的右儿子路径和也是小于limit,就删除这个儿子;
cpp 复制代码
if(node->left && dfs(node->left,limit,sumPath)==false)  { // 左
    node->left = nullptr;
}
cpp 复制代码
if(node->right && dfs(node->right,limit,sumPath)==false)  { // 右
    node->right = nullptr;
}
  • ③ 如果当前节点是非叶子节点node ),且左右儿子都为空,那么就删除 node ,返回 false ;否则,返回 true
cpp 复制代码
return node->left || node->right;

C++代码:

cpp 复制代码
class Solution {
public:
    bool dfs(TreeNode* node,int limit,int sumPath) {
        sumPath += node->val;
        if(node->left == node->right) {
            return sumPath>=limit;
        } 
        if(node->left && dfs(node->left,limit,sumPath)==false)  { // 左
            node->left = nullptr;
        }
        if(node->right && dfs(node->right,limit,sumPath)==false)  { // 右
            node->right = nullptr;
        }
        return node->left || node->right;
    }
    TreeNode* sufficientSubset(TreeNode* root, int limit) {
        return dfs(root,limit,0) ?root:nullptr;
    }
};

① 图解示例一:

② 图解示例三:

方式2**.从 limit 中减去当前节点值**

  • 先比方式1可以少一个参数 sumPath
cpp 复制代码
class Solution {
public:
    bool dfs(TreeNode* node,int limit) {
        limit-=node->val;
        if(node->left == node->right) {
            return limit>0?false:true;
        } 
        if(node->left && dfs(node->left,limit)==false)  { // 左
            node->left = nullptr;
        }
        if(node->right && dfs(node->right,limit)==false)  { // 右
            node->right = nullptr;
        }
        return node->left || node->right;
    }
    TreeNode* sufficientSubset(TreeNode* root, int limit) {
        return dfs(root,limit) ?root:nullptr;
    }
};

方式3. 从 limit 中减去当前节点值( 直接调用 sufficientSubset

  • 如果当前节点是叶子,且此时 limit > 0,说明从根到这个叶子的路径和小于 limit,那么删除这个叶子
  • 如果当前节点不是叶子,那么往下递归,更新它的左儿子为对左儿子调用 sufficientSubset 的结果,更新它的右儿子为对右儿子调用 sufficientSubset的结果
  • 如果左右儿子都为空,那么就删除当前节点,返回空;否则不删,返回当前节点

此段文字来自以下作者

作者:灵茶山艾府

链接:https://leetcode.cn/problems/insufficient-nodes-in-root-to-leaf-paths/description/

cpp 复制代码
class Solution {
public:
    TreeNode* sufficientSubset(TreeNode* root, int limit) {
        limit-=root->val;
        if(root->left == root->right) { // root 是叶子
            return limit > 0 ? nullptr : root;
        } 
        if(root->left)  { // 左
            root->left = sufficientSubset(root->left,limit);
        }
        if(root->right)  { // 右
            root->right = sufficientSubset(root->right,limit);
        }
        // 如果有儿子没被删除,就不删 root,否则删 root
        return root->left || root->right ?root:nullptr;
    }
};

(嘻嘻,仅供参考) 自己又写了一个版本:

cpp 复制代码
class Solution {
public:
    TreeNode* dfs(TreeNode* node,int limit,int sumPath) {
        sumPath += node->val;
        if(node->left == node->right) {
            return sumPath>=limit? node : nullptr;
        } 
        if(node->left)  { // 左
            node->left = dfs(node->left,limit,sumPath);
        }
        if(node->right)  { // 右
            node->right = dfs(node->right,limit,sumPath);
        }
        return node->left || node->right ?node:nullptr;
    }
    TreeNode* sufficientSubset(TreeNode* root, int limit) {
        return dfs(root,limit,0) ? root:nullptr;
    }
};

参考和推荐文章:

1080. 根到叶路径上的不足节点 - 力扣(LeetCode)https://leetcode.cn/problems/insufficient-nodes-in-root-to-leaf-paths/solutions/2278769/jian-ji-xie-fa-diao-yong-zi-shen-pythonj-64lf/

相关推荐
一只码代码的章鱼7 分钟前
数据结构与算法-搜索-剪枝
算法·深度优先·剪枝
roman_日积跬步-终至千里1 小时前
【后端基础】布隆过滤器原理
算法·哈希算法
若兰幽竹1 小时前
【机器学习】多元线性回归算法和正规方程解求解
算法·机器学习·线性回归
鱼力舟1 小时前
【hot100】240搜索二维矩阵
算法
北_鱼3 小时前
支持向量机(SVM):算法讲解与原理推导
算法·机器学习·支持向量机
MZWeiei4 小时前
PTA:运用顺序表实现多项式相加
算法
GISer_Jing4 小时前
Javascript排序算法(冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序)详解
javascript·算法·排序算法
cookies_s_s4 小时前
Linux--进程(进程虚拟地址空间、页表、进程控制、实现简易shell)
linux·运维·服务器·数据结构·c++·算法·哈希算法
丁劲犇5 小时前
碳基生物的悲歌-DeepSeek思考实现Linux动态库递归收集工具
linux·递归·deepseek·ldd
不想编程小谭5 小时前
力扣LeetCode: 2506 统计相似字符串对的数目
c++·算法·leetcode