LeetCode 112. 路径总和 || LeetCode 113. 路径总和ii

LeetCode 112. 路径总和

1、题目

题目链接:112. 路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。

示例 1:
image.png

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:
image.png

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:

  • 树中节点的数目在范围 [0, 5000] 内
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

2、深度优先搜索(递归)

思路

观察要求我们完成的函数,我们可以归纳出它的功能:询问是否存在从当前节点 root 到叶子节点的路径,满足其路径和为 targetSum。
假定从根节点到当前节点的值之和为 val,我们可以将这个大问题转化为一个小问题:是否存在从当前节点的子节点到叶子的路径,满足其路径和为 targetSum - val。
不难发现这满足递归的性质,若当前节点就是叶子节点,那么我们直接判断 targetSum 是否等于 val 即可(因为路径和已经确定,就是当前节点的值,我们只需要判断该路径和是否满足条件)。若当前节点不是叶子节点,我们只需要递归地询问它的子节点是否能满足条件即可。

代码

#include <iostream>using namespace std;//Definition for a binary tree node.
struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode() : val(0), left(nullptr), right(nullptr) {}TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};class Solution {
public:bool traversal(TreeNode* root, int count) {// 如果当前节点是叶子节点,并且count为0,则返回trueif (root->left == nullptr && root->right == nullptr && count == 0) {return true;}// 如果当前节点是叶子节点,但count不为0,则返回falseif (root->left == nullptr && root->right == nullptr && count != 0) {return false;}// 如果左子节点存在if (root->left) {// 将count减去左子节点的值count -= root->left->val;// 递归调用traversal函数处理左子树if (traversal(root->left, count)) {// 如果左子树返回true,则直接返回truereturn true;}// 否则,回溯,恢复count的值,继续处理右子树count += root->left->val;}// 如果右子节点存在if (root->right) {// 将count减去右子节点的值count -= root->right->val;// 递归调用traversal函数处理右子树if (traversal(root->right, count)) {// 如果右子树返回true,则直接返回truereturn true;}// 否则,回溯,恢复count的值count += root->right->val;}// 如果左子树和右子树都没有返回true,则返回falsereturn false;}bool hasPathSum(TreeNode* root, int targetSum) {if (root == nullptr) {return false;}return traversal(root, targetSum - root->val);}
};int main() {Solution s;TreeNode* root = new TreeNode(5);root->left = new TreeNode(4);root->right = new TreeNode(8);root->left->left = new TreeNode(11);root->left->left->left = new TreeNode(7);root->left->left->right = new TreeNode(2);root->right->left = new TreeNode(13);root->right->right = new TreeNode(4);root->right->right->left = new TreeNode(5);root->right->right->right = new TreeNode(1);cout << s.hasPathSum(root, 22) << endl;return 0;
}

复杂度分析

  • 时间复杂度:O(N),其中 N 是树的节点数。对每个节点访问一次。
  • 空间复杂度:O(H),其中 H 是树的高度。空间复杂度主要取决于递归时栈空间的开销,最坏情况下,树呈现链状,空间复杂度为 O(N)。平均情况下树的高度与节点数的对数正相关,空间复杂度为 O(log⁡N)。

3、深度优先搜索(递归精简版)

思路

代码

class Solution {
public:bool hasPathSum(TreeNode* root, int targetSum) {// 如果根节点为空,则返回falseif (!root) {return false;}// 如果根节点没有左子节点和右子节点,并且当前节点的值等于目标值,则返回trueif (!root->left && !root->right && targetSum == root->val) {return true;}// 递归地在左子树中查找是否存在路径和为targetSum - 当前节点值的路径// 或者在右子树中查找是否存在路径和为targetSum - 当前节点值的路径return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);}
};

复杂度分析

  • 时间复杂度:O(N),其中 N 是树的节点数。对每个节点访问一次。
  • 空间复杂度:O(H),其中 H 是树的高度。空间复杂度主要取决于递归时栈空间的开销,最坏情况下,树呈现链状,空间复杂度为 O(N)。平均情况下树的高度与节点数的对数正相关,空间复杂度为 O(log⁡N)。

4、广度优先搜索

思路

我们可以想到使用广度优先搜索的方式,记录从根节点到当前节点的路径和,以防止重复计算。
这样我们使用两个队列,分别存储将要遍历的节点,以及根节点到这些节点的路径和即可。

代码

class Solution {
public:bool hasPathSum(TreeNode *root, int sum) {if (root == nullptr) {return false;}// 使用队列存储节点和节点路径和queue<TreeNode *> queNode;queue<int> queVal;queNode.push(root);queVal.push(root->val);while (!queNode.empty()) {// 取出队列头部的节点和路径和TreeNode *now = queNode.front();int temp = queVal.front();queNode.pop();queVal.pop();// 当前节点为叶子节点if (now->left == nullptr && now->right == nullptr) {// 当前路径和等于目标和if (temp == sum) {return true;}continue;}// 当前节点有左子节点if (now->left != nullptr) {queNode.push(now->left);// 将左子节点的路径和加入队列queVal.push(now->left->val + temp);}// 当前节点有右子节点if (now->right != nullptr) {queNode.push(now->right);// 将右子节点的路径和加入队列queVal.push(now->right->val + temp);}}return false;}
};

复杂度分析

  • 时间复杂度:O(N),其中 N 是树的节点数。对每个节点访问一次。
  • 空间复杂度:O(N),其中 N 是树的节点数。空间复杂度主要取决于队列的开销,队列中的元素个数不会超过树的节点数。

LeetCode 113. 路径总和ii

1、题目

题目链接:113. 路径总和 II
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。

示例 1:
image.png

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]

示例 2:
image.png

输入:root = [1,2,3], targetSum = 5
输出:[]

示例 3:

输入:root = [1,2], targetSum = 0
输出:[]

提示:

  • 树中节点总数在范围 [0, 5000] 内
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

2、深度优先搜索(递归)

思路

我们可以采用深度优先搜索的方式,枚举每一条从根节点到叶子节点的路径。当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。

代码

class solution {
private:vector<vector<int>> result;vector<int> path;void traversal(TreeNode* cur, int count) {// 如果当前节点为叶子节点且计数为0,则将路径加入结果集并返回if (!cur->left && !cur->right && count == 0) {result.push_back(path);return;}// 如果当前节点为叶子节点,则直接返回if (!cur->left && !cur->right) {return;}// 如果当前节点有左子节点if (cur->left) {// 将左子节点的值加入路径path.push_back(cur->left->val);// 更新计数count -= cur->left->val;// 递归遍历左子节点traversal(cur->left, count);// 恢复计数count += cur->left->val;// 将左子节点的值从路径中移除path.pop_back();}// 如果当前节点有右子节点if (cur->right) {// 将右子节点的值加入路径path.push_back(cur->right->val);// 更新计数count -= cur->right->val;// 递归遍历右子节点traversal(cur->right, count);// 恢复计数count += cur->right->val;// 将右子节点的值从路径中移除path.pop_back();}return ;}public:vector<vector<int>> pathSum(TreeNode* root, int sum) {result.clear();path.clear();// 如果根节点为空,则直接返回空结果if (root == nullptr) {return result;}// 将根节点的值加入路径中path.push_back(root->val);// 调用遍历函数,传入当前节点和剩余的目标和traversal(root, sum - root->val);// 返回最终结果return result;}
};

复杂度分析

  • 时间复杂度: O(n^2)
  • 空间复杂度: O(n)

3、广度优先搜索

思路

我们也可以采用广度优先搜索的方式,遍历这棵树。当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。
为了节省空间,我们使用哈希表记录树中的每一个节点的父节点。每次找到一个满足条件的节点,我们就从该节点出发不断向父节点迭代,即可还原出从根节点到当前节点的路径。

代码

class Solution {
public:vector<vector<int>> ret;unordered_map<TreeNode*, TreeNode*> parent;void getPath(TreeNode* node) {// 创建一个临时向量,用于存储路径上的节点值vector<int> tmp;// 当节点不为空时,继续循环while (node != nullptr) {// 将当前节点的值添加到临时向量中tmp.emplace_back(node->val);// 将当前节点更新为其父节点node = parent[node];}// 反转临时向量,使其按从根节点到叶子节点的顺序排列reverse(tmp.begin(), tmp.end());// 将反转后的路径添加到结果向量中ret.emplace_back(tmp);}vector<vector<int>> pathSum(TreeNode* root, int targetSum) {if (root == nullptr) {return ret;}// 使用队列进行广度优先搜索queue<TreeNode*> que_node;queue<int> que_sum;que_node.emplace(root);que_sum.emplace(0);while (!que_node.empty()) {TreeNode* node = que_node.front();que_node.pop();int rec = que_sum.front() + node->val;que_sum.pop();// 如果当前节点是叶子节点if (node->left == nullptr && node->right == nullptr) {// 如果路径和等于目标和if (rec == targetSum) {// 调用getPath函数获取路径getPath(node);}} else {// 如果左子节点不为空if (node->left != nullptr) {// 记录父节点parent[node->left] = node;// 将左子节点和路径和加入队列que_node.emplace(node->left);que_sum.emplace(rec);}// 如果右子节点不为空if (node->right != nullptr) {// 记录父节点parent[node->right] = node;// 将右子节点和路径和加入队列que_node.emplace(node->right);que_sum.emplace(rec);}}}return ret;}
};

复杂度分析

  • 时间复杂度: O(n^2)
  • 空间复杂度: O(n)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/687961.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Yii2 自动生成php代码

文档地址&#xff1a;入门&#xff08;Getting Started&#xff09;: 用 Gii 生成代码&#xff08;Generating Code with Gii&#xff09; - Yii 2.0 权威指南 - 文档 - Yii Framework 中文网 找到配置文件&#xff0c;以我的项目为例&#xff1a; 因为的是开启了路由美化所以访…

二级等保与三级等保的区别有哪些

二级等保和三级等保的区别主要体现在保护能力、安全要求、监管严格程度等方面。以下是根据提供的搜索结果中关于二级和三级等保的具体差异&#xff1a; 1. 保护能力&#xff1a; 二级等保要求信息系统能够防护来自外部小型组织的威胁&#xff0c;发现重要的安全漏洞和事件&…

浴厕收拾干净能留财,你可知道?

常常给客户看风水&#xff0c;积累了很多的经验。虽然峰民是一位说话很直的人&#xff0c;不喜欢做作&#xff0c;不喜欢遮掩。但在工作上&#xff0c;在学术上&#xff0c;是不马虎的。每次峰民分析给大家的都是很实用&#xff0c;很经典的风水常识。 今天谈谈浴厕风水。作为…

经开区创维汽车车辆交接仪式顺利举行,守护绿色出行助力低碳发展

5月10日&#xff0c;“创维新能源汽车进机关”交车仪式于徐州顺利举行&#xff0c;20辆创维EV6 II正式交付经开区政府投入使用。经开区陈琳副书记、党政办公室副主任张驰主任、经开区公车管理平台苑忠民科长、创维汽车总裁、联合创始人吴龙八先生、创维汽车营销公司总经理饶总先…

C++学习第二十八课:C++ 中的智能指针详解

在 C 中&#xff0c;内存管理是每个程序员都需要面对的问题。在处理动态分配的内存时&#xff0c;如果忘记释放内存&#xff0c;可能会导致内存泄漏。为了解决这个问题&#xff0c;C11 引入了智能指针的概念。本文将详细介绍 C 中使用智能指针的方法&#xff0c;并结合实际案例…

护网中经常使用的一些工具(非常详细)零基础入门到精通,收藏这一篇就够了

通用工具 工具类型工具地址内网扫描https://github.com/shadow1ng/fscan哥斯拉Webshell管理https://github.com/BeichenDream/Godzill aARL 资产侦察灯塔https://github.com/TophantTechnology/AR Laliyun-accesskey-Toolshttps://github.com/mrknow001/aliyun-acc esskey-Too…

sqli-labs靶场第十四关

目录 1&#xff1a;分析 找闭合符&#xff1a; 2&#xff1a;开始注入 报错注入&#xff1a; 注入数据库名&#xff1a; 注入表名&#xff1a; 注入列名&#xff1a; 注入具体值&#xff1a; 1&#xff1a;分析 经过我们的实验发现当我们输入的密码后面存在双引号时会报…

「网络流 24 题」负载平衡 【费用流】

「网络流 24 题」负载平衡 思路 首先我们从源点向每个仓库连边&#xff0c;容量为 a i a_i ai​&#xff0c;费用为 0 0 0&#xff1b;既然所有仓库物品相同&#xff0c;那么数量一定是总物品的平均值&#xff0c;我们提前算出来 a v g avg avg&#xff0c;然后从每个仓库向…

批量扩充库存地点操作手册

业务场景 售后库存需要扩充库存地点,避免影响生产MRP运算 前提条件 必须有MMSC_MASS权限 数据导出 1、前台可以通过MB52导出某库存地点下的所有物料信息 2、由IT导出该部分数据,供业务部门使用。 案例: 以下以3008仓扩充3020仓为例 操作指引: 输入事务码MMSC_MASS…

【QA】Java集合常用的函数

文章目录 前言Collection接口通用函数 | Collections工具类通用函数 | List接口 Set接口List接口ArrayListLinkedList Set接口TreeSetHashSetLinkedHashSet Map接口通用函数TreeMapHashMapLinkedHashMap 前言 本文介绍Java集合中常用的函数。 Collection接口 通用函数 | Co…

408算法题专项-2019年

题目&#xff1a; 分析&#xff1a;要求空间复杂度为O&#xff08;1&#xff09;&#xff0c;我们可以逆向假设可以开空间&#xff0c;得出一种思路&#xff0c;然后对这种思路优化空间即可得到O&#xff08;1&#xff09; 思路一&#xff1a;假设开空间 思考&#xff1a;再开…

geotrust dv通配符证书800

Geotrust是成立时间较久的正规CA认证机构&#xff0c;在过去的几十年间颁发了无数的SSL证书&#xff0c;这些SSL证书被各个开发者使用&#xff0c;受到大多数浏览器的信任。而Geotrust旗下的DV通配符证书因其广泛的应用范围受到了用户的青睐。今天就随SSL盾小编了解Geotrust旗下…