路径总和

力扣题目链接(opens new window)

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

说明: 叶子节点是指没有子节点的节点。

示例: 给定如下二叉树,以及目标和 sum = 22,

返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2

思路

用递归的方法做,传入target值,没遍历到一个节点就与其做差

如果最后遍历到叶子节点时target值变成0,那么就说明本条路径的节点之和满足target值

递归法

1、确定递归函数参数和返回值

输入肯定是根节点了,因为还需要把target传入,所以还需要一个计数变量int count

那么,返回值如何确定?或者说需不需要返回值?

这里引申一个问题:递归函数什么时候需要返回值?

可以总结为如下三点:

  • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(113.路径总和ii)
  • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (236. 二叉树的最近公共祖先 (opens new window)
  • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)

因为题目只要求我们找出一条符合路径值等于target的路径,所以我们有可能不需要完整遍历整颗二叉树。且一旦找到满足条件的路径,需要及时返回

因此递归函数的返回值应该是bool

bool traversal(TreeNode* node, int count){

}

2、确定终止条件

这里要考虑如何统计全部路径节点的值

注意,不要使用累加的方式(即遍历一个节点加一个,然后最后再判断是否满足target),这种方式虽然很容易想到,但是代码实现比较麻烦

因此,我们将count设置为target的大小,让其在递归遍历的过程中递减

那么结束的条件就有以下两种:

1、遇到叶子节点(遍历结束),且当前count已经递减至0

2、遇到叶子节点(遍历结束)但count不为0

bool traversal(TreeNode* node, int count){
if(node->left == NULL && node->right == NULL && count == 0) return true;
if(node->left == NULL && node->right == NULL) return false;
}

3、确定单层处理逻辑

调用递归函数,当我们找到满足条件的路径或者已经到达叶子节点后,需要通过回溯将count值再加回原来的初值状态(等于target)

因为递归函数设置了返回值,如果我们找到符合条件的路径,就可以立刻返回true

没找到就返回false

bool traversal(TreeNode* node, int count){
//确定终止条件
//到叶子节点并找到路径
if(node->left == NULL && node->right == NULL && count == 0) return true;
//到叶子节点但没找到路径
if(node->left == NULL && node->right == NULL) return false; //确定单层处理逻辑
//调用递归函数
if(node->left){
count -= node->left->val;//不断递减
//如果递归函数返回true,则找到路径,可以直接返回true
if(traversal(node->left, count)) return true;
count += node->left->val;//回溯,撤销处理结果
}
if(node->right){
count -= node->right->val;//不断递减
//如果递归函数返回true,则找到路径,可以直接返回true
if(traversal(node->right, count)) return true;
count += node->right->val;//回溯,撤销处理结果
}
return false;//没找到返回false
}
代码
class Solution {
public:
//使用递归,遍历出所有路径,将路径之和与target比较
//确定递归函数的参数和返回值
bool traversal(TreeNode* node, int count){
//确定终止条件
//到叶子节点并找到路径
if(node->left == NULL && node->right == NULL && count == 0) return true;
//到叶子节点但没找到路径
if(node->left == NULL && node->right == NULL) return false; //确定单层处理逻辑
//调用递归函数
if(node->left){
count -= node->left->val;//不断递减
//如果递归函数返回true,则找到路径,可以直接返回true
if(traversal(node->left, count)) return true;
count += node->left->val;//回溯,撤销处理结果
}
if(node->right){
count -= node->right->val;//不断递减
//如果递归函数返回true,则找到路径,可以直接返回true
if(traversal(node->right, count)) return true;
count += node->right->val;//回溯,撤销处理结果
}
return false;//没找到返回false }
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL) return false;//先判断根节点是否为空
int count = targetSum - root->val;//根节点也算在路径中,所以count也要减去其值
return traversal(root, count);
}
};

迭代法

TBD

注意点

1、在主函数中,必须确认根节点是否为空,否则必错

2、在主函数中,不要忘记把根节点的值加入递减计数

二刷问题

递归函数的停止条件顺序不能互换
class Solution {
public:
//使用递归,遍历出所有路径,将路径之和与target比较
//确定递归函数的参数和返回值
bool traversal(TreeNode* node, int count){
//确定终止条件
//到叶子节点并找到路径
if(node->left == NULL && node->right == NULL && count == 0) return true;
//到叶子节点但没找到路径
if(node->left == NULL && node->right == NULL) return false;
//错误:
//if(node->left == NULL && node->right == NULL) return false;
//if(node->left == NULL && node->right == NULL && count == 0) return true;

这是因为当根节点没有左右子树时,需要特别处理。

在第一个if语句中,如果count等于0,则说明从根节点到叶子节点的路径上所有节点之和正好等于目标值targetSum,因此返回true。在第二个if语句中,如果没有匹配到第一个if语句的条件,则说明到达了叶子节点但是总和不等于目标值targetSum,此时应该返回false,表示没有符合条件的路径。

如果将两个if语句的顺序互换,会导致遍历到叶子节点时无法正确判断是否存在一条符合条件的路径。当叶子节点没有左右子树时,先进入第二个if语句中的条件判断,返回false,而不检查路径和是否等于目标值,因此会误判。

也就是说,到了叶子节点必须先判断一路上的路径值,如果满足条件就要返回true,如果上述代码顺序颠倒,则会把一些正确的满足条件的结果漏掉,直接判断为false,从而导致错误

记得回溯

到当前叶子节点的路径不满足条件时要往回走,记得要再把count加回来,也就是进行回溯操作

路径总和II

力扣题目链接(opens new window)

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

示例: 给定如下二叉树,以及目标和 sum = 22,

思路

因为题目要求的是返回所有路径节点和满足target的路径

所以,我们需要遍历整棵树

按照前面的讨论,这种情况下,递归函数不需要返回值(因为不需要满足条件就返回)

这里只介绍递归法,迭代法太麻烦了,写了也记不住

本题中,用于存放路径的数组path和存放结果的二维数组res需要定义在递归函数外

1、确定递归函数的参数和返回值

路径总和的基本思路一致,输入是根节点和一个计数变量(通过递减来寻找满足条件的路径)

返回值是不需要的

2、确定终止条件

终止条件也与路径总和一致,共两种:

  • 遇到叶子节点,且路径满足条件
  • 遇到叶子节点但不满足条件

3、确定单层逻辑

在这一步中,我们除了对计数变量count进行递减和回溯,还需要将当前节点的值加入路径数组path中,并在回溯时将元素pop掉

代码

class Solution {
public:
vector<vector<int>> res;//存放最值输出结果
vector<int> path;//存放当前满足条件的路径
//确定递归函数的参数和返回值
//输入参数为根节点和计数变量
//本题中,需要找出所有路径节点之和满足条件的路径,因此需要遍历整棵树,故没有返回值
void traversal(TreeNode* node, int count){
//确定终止条件
//遇到叶子节点,且路径满足条件
if(node->left == NULL && node->right == NULL && count == 0){
//保存此时path中记录的路径
res.push_back(path);
return;
}
//遇到叶子节点但不满足条件
if(node->left == NULL && node->right == NULL) return; //确定单层逻辑
if(node->left){
//保存当前节点值到path
path.push_back(node->left->val);
//计数递减
count -= node->left->val;
traversal(node->left, count);//调用递归
//回溯,包括计数变量和路径数组
count += node->left->val;
path.pop_back();
} if(node->right){
//保存当前节点值到path
path.push_back(node->right->val);
//计数递减
count -= node->right->val;
traversal(node->right, count);//调用递归
//回溯,包括计数变量和路径数组
count += node->right->val;
path.pop_back();
}
} vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
//确认根节点不为空
if(root == NULL) return res;
int count = targetSum - root->val;//将根节点加入递减计数
path.push_back(root->val);//将根节点值加入数组
traversal(root, count);
return res;
}
};

注意点

1、用于存放路径的数组path和存放结果的二维数组res需要定义在递归函数外

2、在主函数中,必须确认根节点是否为空

3、在主函数中,不要忘记把根节点的值加入递减计数,以及路径数组

求根节点到叶节点数字之和

https://leetcode.cn/problems/sum-root-to-leaf-numbers/

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。

每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。

计算从根节点到叶节点生成的 所有数字之和 。

叶节点 是指没有子节点的节点。

示例 1:

输入:root = [1,2,3]

输出:25

解释:

从根到叶子节点路径 1->2 代表数字 12

从根到叶子节点路径 1->3 代表数字 13

因此,数字总和 = 12 + 13 = 25

示例 2:

输入:root = [4,9,0,5,1]

输出:1026

解释:

从根到叶子节点路径 4->9->5 代表数字 495

从根到叶子节点路径 4->9->1 代表数字 491

从根到叶子节点路径 4->0 代表数字 40

因此,数字总和 = 495 + 491 + 40 = 1026

提示:

树中节点的数目在范围 [1, 1000] 内

0 <= Node.val <= 9

树的深度不超过 10

思路

路径总和II类似,因为要遍历完二叉树的所有路径,因此递归函数无需返回值,只需去操控一个vector即可

在本题中,以何种方式遍历二叉树是无所谓的

下面来三部曲

确定递归函数+终止条件

前面说了,因为要遍历整个树,所以递归函数不需要返回值。显然,其输入是当前节点

class Solution {
private:
void traversal(TreeNode* cur){
if(cur->left == nullptr && cur->right == nullptr){//遍历到叶子节点
res += vec2Int(path);
return;
} }
public:
int sumNumbers(TreeNode* root) {
}
};

当前节点为叶子节点时,将一路上收集到的各个节点的值(path数组)转换为整数

(注意叶子节点的判断依据:即左右子节点为空)

此时,需要一个辅助函数vec2Int,可以注意一下该函数的实现,这是一种整数转换的方法

class Solution {
private:
int res = 0;
vector<int> path;
int vec2Int(vector<int>& path){//用于处理数组path,将其转换为整数
int sum = 0;
for(int i = 0; i < path.size(); ++i){
sum = sum*10 + path[i];
}
return sum;
}
void traversal(TreeNode* cur){
if(cur->left == nullptr && cur->right == nullptr){//遍历到叶子节点
res += vec2Int(path);
return;
} }
public:
int sumNumbers(TreeNode* root) {
}
};
确定单层处理逻辑

这里就跟路径总和II的处理方式一样了,先判断左右子节点是否存在,如果存在就将其值加入路径数组

class Solution {
private:
int res = 0;
vector<int> path;
int vec2Int(vector<int>& path){//用于处理数组path,将其转换为整数
int sum = 0;
for(int i = 0; i < path.size(); ++i){
sum = sum*10 + path[i];
}
return sum;
} void traversal(TreeNode* cur){
if(cur->left == nullptr && cur->right == nullptr){//遍历到叶子节点
res += vec2Int(path);
return;
} if(cur->left){//递归遍历左分支
path.push_back(cur->left->val);
traversal(cur->left);
path.pop_back();
} if(cur->right){//递归遍历右分支
path.push_back(cur->right->val);
traversal(cur->right);
path.pop_back();
}
return;
}
public:
int sumNumbers(TreeNode* root) {
}
};

注意记得回溯

代码

class Solution {
private:
int res = 0;
vector<int> path;
int vec2Int(vector<int>& path){//用于处理数组path,将其转换为整数
int sum = 0;
for(int i = 0; i < path.size(); ++i){
sum = sum*10 + path[i];
}
return sum;
} void traversal(TreeNode* cur){
if(cur->left == nullptr && cur->right == nullptr){//遍历到叶子节点
res += vec2Int(path);
return;
} if(cur->left){//递归遍历左分支
path.push_back(cur->left->val);
traversal(cur->left);
path.pop_back();
} if(cur->right){//递归遍历右分支
path.push_back(cur->right->val);
traversal(cur->right);
path.pop_back();
}
return;
}
public:
int sumNumbers(TreeNode* root) {
if(root == nullptr) return 0;
path.push_back(root->val);//不要忘了先把当前节点压入数组
traversal(root);
return res;
}
};

【LeetCode二叉树#09】路径总和I+II,以及求根节点到叶节点数字之和(回溯回溯,还是™的回溯)的更多相关文章

  1. 【LeetCode】113. 路径总和 II

    题目 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标和 sum = 22, 5 / \ ...

  2. 【LeetCode】437. 路径总和 III

    437. 路径总和 III 给定一个二叉树,它的每个结点都存放着一个整数值. 找出路径和等于给定数值的路径总数. 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点 ...

  3. 【LeetCode】112. 路径总和 Path Sum 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 DFS 回溯 BFS 栈 日期 题目地址:https ...

  4. [LeetCode 112 113] - 路径和I & II (Path Sum I & II)

    问题 给出一棵二叉树及一个和值,检查该树是否存在一条根到叶子的路径,该路径经过的所有节点值的和等于给出的和值. 例如, 给出以下二叉树及和值22: 5         / \       4  8  ...

  5. LeetCode 5129. 下降路径最小和 II Minimum Falling Path Sum II

    地址 https://leetcode-cn.com/contest/biweekly-contest-15/problems/minimum-falling-path-sum-ii/ 题目描述给你一 ...

  6. LeetCode【112. 路径总和】

    思路就是从根节点开始向下选节点,依次与sum比较大小,若小,则向下选左右节点其中一个,若大,则接下来判断是否是叶子节点,若是,则返回false 若不是,则上一步选另一节点,再将上述重新执行. 对于叶子 ...

  7. lintcode:二叉树的路径和

    题目 给定一个二叉树,找出所有路径中各节点相加总和等于给定 目标值 的路径. 一个有效的路径,指的是从根节点到叶节点的路径. 解题 下面有个小bug 最后比较的时候是叶子节点为空,左右都有叶子结点,所 ...

  8. LintCode-376.二叉树的路径和

    二叉树的路径和 给定一个二叉树,找出所有路径中各节点相加总和等于给定 目标值 的路径. 一个有效的路径,指的是从根节点到叶节点的路径. 样例 给定一个二叉树,和 目标值 = 5: 返回: [      ...

  9. Java实现求二叉树的路径和

    题: 解: 这道题考的是如何找出一个二叉树里所有的序列. 我的思路是先从根节点开始遍历,找出所有的子节点,因为每个子节点只有一个父节点,再根据每个子节点向上遍历找出所有的序列,再判断序列的总和. 这样 ...

  10. [Leetcode] Path Sum路径和

    Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all ...

随机推荐

  1. 部分信创CPU算力与IntelCPU的简单比较

    部分信创CPU算力与IntelCPU的简单比较 摘要 最近一直想查看一下国产和非国产的CPU的性能比较 从最开始学习研究 sysbench 到周五晚上开始学习 stress-ng 今天查看github ...

  2. runc网络与systemd管理runc应用

    1. 创建网络命名空间 ip netns add <ns> # ns是自定义网络空间名 # 例如: ip netns add haproxy # 创建一个给 haproxy使用的网络命名空 ...

  3. CCFLOW源码解读系列01-----发起流程

    1.发起流程 发起流程时主要做了两件事:一是写入业务数据表,二是新建一条审批流程记录. 发起流程的方法 public static Int64 Node_CreateStartNodeWork(str ...

  4. windbg-windows调试工具来抓端游crash dump

    windbg下载有两种方式: Install WinDbg - Windows drivers | Microsoft Learn 从微软应用商店下载 dump上的windows的局部变量解析部分进行 ...

  5. 提高Android Studio的编译速度(更快出包减少等待)

    硬件和软件的准备 对于经常要出包而且一次要出多个渠道APK的同事来说,每次漫长的打包等待是一件消耗生命且无意义事情. google官方提高编译速度的文档:https://developer.andro ...

  6. Unity2020或Unity2019安装后无法启动

    无法启动Unity 下载国际版的Unity2020,双击Unity.exe无法启动,通过Unity Hub也无法启动 ​ 原因 通过查看unity hub的日志发现Unity 启动的时候会检查 lie ...

  7. Go语言的原子操作atomic

    atomic 原子操作 Go中原子操作的支持 CompareAndSwap(CAS) Swap(交换) Add(增加或减少) Load(原子读取) Store(原子写入) 原子操作与互斥锁的区别 at ...

  8. NLP专栏简介:数据增强、智能标注、意图识别算法|多分类算法、文本信息抽取、多模态信息抽取、可解释性分析、性能调优、模型压缩算法等

    NLP专栏简介:数据增强.智能标注.意图识别算法|多分类算法.文本信息抽取.多模态信息抽取.可解释性分析.性能调优.模型压缩算法等 专栏链接:NLP领域知识+项目+码源+方案设计 订阅本专栏你能获得什 ...

  9. locate命令找不到,但是实际文件存在的情况

      locate和find命令都是linux下常用的搜索命令,但是locate命令是从一个数据库里面搜索的,它的速度比find查找要快上不少.如果存在某个文件用locate查不到的话,那么可以用upd ...

  10. 英特尔发布酷睿Ultra移动处理器:Intel 4制程工艺、AI性能飙升

    英特尔今日发布了第一代酷睿Ultra移动处理器,是首款基于Intel 4制程工艺打造的处理器. 据了解,英特尔酷睿Ultra采用了英特尔首个用于客户端的片上AI加速器"神经网络处理单元(NP ...