LeetCode113. 路径总和 II

news2024/9/23 11:23:50

113. 路径总和 II

文章目录

      • [113. 路径总和 II](https://leetcode.cn/problems/path-sum-ii/)
        • 一、题目
        • 二、题解
          • 方法一:递归
          • 另一种递归版本
          • 方法二:迭代


一、题目

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

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

示例 1:

img

输入: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:

img

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

示例 3:

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

提示:

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

二、题解

方法一:递归

算法思路

  1. 我们需要遍历二叉树中所有从根节点到叶子节点的路径,以找出满足路径和等于目标和的路径。这提示我们可以使用深度优先搜索(DFS)来遍历树的所有可能路径。

  2. 我们可以在递归过程中维护一个当前路径的和 count,从根节点开始,每到一个节点,我们将该节点的值在count 上进行减法处理。

  3. 如果当前节点是叶子节点(即没有左右子节点),我们检查 count 是否等于0。如果是,我们将当前路径添加到结果中。

  4. 在递归过程中,我们需要传递当前路径 path,结果数组 result,当前路径的和 count,以及当前节点 root

具体实现

class Solution {
public:
    void findPath(TreeNode *root, vector<vector<int>>&result, vector<int>& path, int count){
        if(root == nullptr) return;
        
        path.push_back(root->val);
        count -= root->val;
        
        if(count == 0 && !root->left && !root->right){
            result.push_back(path);
        }
        
        if(root->left){
            findPath(root->left, result, path, count);
        }
        
        if(root->right){
            findPath(root->right, result, path, count);
        }
        
        path.pop_back(); // 回溯,移除当前节点,尝试其他路径
    }
    
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<vector<int>> result;
        vector<int> path;
        
        if(root == nullptr) return result;
        
        findPath(root, result, path, targetSum);
        
        return result;
    }
};

算法分析

  • 时间复杂度:遍历整个二叉树,每个节点只访问一次,所以时间复杂度为 O(N),其中 N 是树中的节点数。

  • 空间复杂度:在递归过程中,我们使用了 path 数组来存储当前路径,最坏情况下,路径长度达到树的深度,所以空间复杂度为 O(H),其中 H 是树的高度。在结果数组中,我们存储了满足条件的路径,最坏情况下可能会有 O(N) 个路径,所以结果数组的空间复杂度也是 O(N)。

另一种递归版本

因为cpp的特性,通过去掉 vector<int>& 中的引用符号,我可以在每个递归层次中都创建了一个新的 path 向量,这样可以确保在不同的递归路径之间不会共享相同的 path 对象,相当于进行了回溯。

/**
 * 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:
    void findPath(TreeNode *root, vector<vector<int>>&result, vector<int> path, int count){
        if(root == nullptr) return;
        path.push_back(root->val);
        count -= root->val;
        if(count == 0 && !root->left && !root->right){
            result.push_back(path);
        }
        if(root->left){
            findPath(root->left, result, path, count);
        }
        if(root->right){
            findPath(root->right, result, path, count);
        }
    }
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<vector<int>> result;
        vector<int> path;
        if(root == nullptr) return result;
        int count = targetSum;
        findPath(root, result, path, count);
        return result;
    }
};
方法二:迭代

算法思路

  1. 我们使用中序遍历来遍历树的节点,同时维护两个栈:St 用于保存遍历节点的信息,pathSt 用于保存当前路径的节点值。
  2. 初始时,我们将根节点 root 和对应的路径值 root->val 入栈,表示从根节点开始的路径。
  3. 在每一次循环中,我们从 St 栈中取出一个节点,同时从 pathSt 中取出与该节点对应的路径。
  4. 我们检查该节点是否为叶子节点,如果是叶子节点并且路径值等于 targetSum,则将该路径保存到 result 中。
  5. 如果不是叶子节点,我们将其左子节点和右子节点(如果存在的话)入栈,并更新路径值。
  6. 重复以上步骤直至 St 栈为空。

具体实现

class Solution {
public:
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<vector<int>> result; // 用于存储符合条件的路径
        stack<pair<TreeNode*, int>> St; // 用于深度优先搜索的栈,同时保存节点和路径和
        stack<vector<int>> pathSt; // 用于保存路径的栈
        if (root == nullptr) return result; // 处理根节点为空的情况

        // 初始化,将根节点和路径和入栈
        St.push(pair<TreeNode*, int>(root, root->val));
        vector<int> temp;
        temp.push_back(root->val);
        pathSt.push(temp);

        while (!St.empty()) {
            pair<TreeNode*, int> node = St.top(); St.pop(); // 弹出栈顶节点
            vector<int> pathT = pathSt.top(); pathSt.pop(); // 弹出栈顶路径

            // 如果是叶子节点且路径和等于targetSum,将路径保存到result中
            if (!node.first->left && !node.first->right && node.second == targetSum) {
                result.push_back(pathT);
            }

            // 将右子节点入栈,更新路径和,并将路径入栈
            if (node.first->right) {
                St.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));
                vector<int> newPathT = pathT;
                newPathT.push_back(node.first->right->val);
                pathSt.push(newPathT);
            }

            // 将左子节点入栈,更新路径和,并将路径入栈
            if (node.first->left) {
                St.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));
                vector<int> newPathT = pathT;
                newPathT.push_back(node.first->left->val);
                pathSt.push(newPathT);
            }
        }
        return result; // 返回所有满足条件的路径
    }
};

算法分析

  • 时间复杂度:每个节点最多访问一次,所以时间复杂度为 O(N),其中 N 是树的节点数。
  • 空间复杂度:栈的最大空间取决于树的高度,所以空间复杂度为 O(H),其中 H 是树的高度。

简化版本

class Solution {
public:
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<vector<int>> result;
        vector<int> path;
        stack<TreeNode*> TreeSt;
        stack<vector<int>> pathSt;
        if(root == nullptr) return result;
        
        // 初始化
        TreeSt.push(root);
        vector<int> temp;
        temp.push_back(root->val);
        pathSt.push(temp);
        
        while(!TreeSt.empty()){
            TreeNode *node = TreeSt.top();TreeSt.pop();
            vector<int> pathT = pathSt.top();pathSt.pop();
            if(!node->left && !node->right && accumulate(pathT.begin(), pathT.end(), 0) == targetSum) {	//这里直接计算路径上的总和
                result.push_back(pathT);
            }
            if(node->right){
                TreeSt.push(node->right);
                vector<int> newPathT = pathT; 
                newPathT.push_back(node->right->val);
                pathSt.push(newPathT);
            }
            if(node->left){
                TreeSt.push(node->left);
                vector<int> newPathT = pathT; 
                newPathT.push_back(node->left->val);
                pathSt.push(newPathT);
            }
        }
        return result;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter 实现按位置大小比例布局的控件

文章目录 前言一、如何实现&#xff1f;1、数值转成分数2、RowFlexible布局横向3、ColumnFlexible布局纵向 二、完整代码三、使用示例1、基本用法2、四分屏3、六分屏4、八分屏5、九分屏6、414分屏 总结 前言 做视频监控项目时需要需要展示多分屏&#xff0c;比如2x2、3x3、414…

vue2-vue中key的原理

vue中key是什么&#xff1f;它有什么作用&#xff1f;原理是什么&#xff1f; 1、key是什么&#xff1f; 先考虑两个实际场景 当我们使用v-for时&#xff0c;需要给单元加上key 用new Date()生成的时间戳作为key&#xff0c;手动强制触发重新渲染。 在上面两种场景中&#xf…

一百四十三、Linux——Linux的CentOS 7系统语言由中文改成英文

一、目的 之前安装CentOS 7系统的时候把语言设置成中文&#xff0c;结果Linux文件夹命名出现中文乱码的问题&#xff0c;于是决定把Linux系统语言由中文改成英文 二、实施步骤 &#xff08;一&#xff09;到etc目录下&#xff0c;找到配置文件locale.conf # cd /etc/ # ls…

总结七大排序!

排序总览 外部排序&#xff1a;依赖硬盘&#xff08;外部存储器&#xff09;进行的排序。对于数据集合的要求特别高&#xff0c;只能在特定场合下使用&#xff08;比如一个省的高考成绩排序&#xff09;。包括桶排序&#xff0c;基数排序&#xff0c;计数排序&#xff0c;都是o…

C++ STL快速应用

STL 容器 STL容器有共同的操作接口&#xff0c;包括初始化操作、判空、查看大小、比较元素、销毁、交换&#xff0c;这些操作都是一样的接口。 对于访问遍历元素&#xff08;增删改查&#xff09;&#xff0c;都可以使用迭代器&#xff08;正向&#xff09;进行操作&#xff0c…

信号平滑或移动平均滤波研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【雕爷学编程】Arduino动手做(184)---快餐盒盖,极低成本搭建机器人实验平台2

吃完快餐粥&#xff0c;除了粥的味道不错之外&#xff0c;我对个快餐盒的圆盖子产生了兴趣&#xff0c;能否做个极低成本的简易机器人呢&#xff1f;也许只需要二十元左右 知识点&#xff1a;轮子&#xff08;wheel&#xff09; 中国词语。是用不同材料制成的圆形滚动物体。简…

JDK17环境下安装Nacos

1.配置好jdk17环境 命令台java -version显示17版本 2.下载并安装Nacos 下载地址&#xff1a;Releases alibaba/nacos GitHub 安装完本地解压 解压完到nacos的bin目录下&#xff0c;执行.\startup.cmd -m standalone启动即可。 用过好几种方式&#xff0c;比如启动startup…

一百四十五、Kettle——查看Kettle在Windows本地和在Linux上生成的.kettle文件夹位置

&#xff08;一&#xff09;目的 查看kettle连数据库后自动生成的.kettle文件夹在Windows本地和在Linux中的位置&#xff0c; 这个文件很重要&#xff01;&#xff01;&#xff01; &#xff08;二&#xff09;.kettle文件夹在Windows本地的位置 C:\Users\Administrator\.k…

ClickHouse SQL与引擎--基本使用(一)

1.查看所有的数据库 show databases; 2.创建库 CREATE DATABASE zabbix ENGINE Ordinary; ATTACH DATABASE ck_test ENGINE Ordinary;3.创建本地表 CREATE TABLE IF NOT EXISTS test01(id UInt64,name String,time UInt64,age UInt8,flag UInt8 ) ENGINE MergeTree PARTI…

英特尔傲腾CAS报错unknown error cache acceleration software could not start cache

英特尔傲腾CAS报错unknown error cache acceleration software could not start cache 文章目录 英特尔傲腾CAS报错unknown error cache acceleration software could not start cache我是怎么遇到这个问题的我是如何解决的实验步骤打Primo Cache蓝屏补丁拔掉原来的系统盘开关机…

【力扣】21. 合并两个有序链表 <链表指针>

【力扣】21. 合并两个有序链表 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例1 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;…

Java List(列表)

List 是一个有序、可重复的集合&#xff0c;集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素&#xff0c;可以通过索引来访问指定位置的集合元素。List 集合默认按元素的添加顺序设置元素的索引&#xff0c;第一个添加到 List 集合中的元素的索引为 0&#xff…

如何从 Android 设备恢复已删除的文件?

从 Android 设备恢复已删除的文件很简单&#xff0c;但您需要了解内部恢复和SD 卡恢复之间的区别。 目前销售的大多数 Android 设备都配备了 SD 卡插槽&#xff08;通常为 microSD&#xff09;&#xff0c;可以轻松添加额外的存储空间。该存储空间可用于存储照片、视频、文档&a…

机器学习概述及其主要算法

目录 1、什么是机器学习 2、数据集 2.1、结构 3、算法分类 4、算法简介 4.1、K-近邻算法 4.2、贝叶斯分类 4.3、决策树和随机森林 4.4、逻辑回归 4.5、神经网络 4.6、线性回归 4.7、岭回归 4.8、K-means 5、机器学习开发流程 6、学习框架 1、什么是机器学习 机器…

SprinMVC获取请求参数

SprinMVC获取请求参数 Spring MVC 提供的获取请求参数的方式 通过 HttpServletRequest 获取请求参数通过控制器方法的形参获取请求参数使用 RequestParam 注解获取请求参数通过实体类对象获取请求参数&#xff08;推荐&#xff09; 通过ServlstAPI获取 将HttpServletRequest…

Qt开发,去掉Qt生成动态库的版本号

一、问题描述 1、Qt在生成动态库时&#xff0c;于Mac下默认生成带版本号的库&#xff0c;于Windows下默认生成不带版本号的库。 二、问题分析 生成带完整版本号的库文件及相关软链接 三、解决方案 以插件方式生成动态库&#xff0c;在.pro文件中添加以下配置参数 CONFIG …

C高级【day3】

思维导图&#xff1a; 判断家目录下&#xff0c;普通文件的个数和目录文件的个数&#xff1a; #!/bin/bashvar1(ls -l ~/ | cut -d r -f 1 | grep -i -) var2(ls -l ~/ | cut -d r -f 1 | grep -i d) #echo ${var1[*]} #echo ${var2[*]}echo 普通文件个数&#xff1a;${#var…

vue2-$nextTick有什么作用?

1、$nextTick是什么&#xff1f; 官方定义&#xff1a;在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法&#xff0c;获取更新后的DOM。 解释&#xff1a;Vue在更新DOM时是异步执行的&#xff0c;当数据发生变化时&#xff0c;Vue将开启一个异步更新的队…

Linux学习之正则表达式元字符和grep命令

cat /etc/redhat-release看到操作系统的版本是CentOS Linux release 7.6.1810 (Core)&#xff0c;uname -r可以看到内核版本是3.10.0-957.21.3.el7.x86_64。 正则表达式是一种搜索字符串的模式&#xff0c;通俗点理解&#xff0c;也就是普通字符和元字符共同组成的字符集合匹…