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,则返回true
        if (root->left == nullptr && root->right == nullptr && count == 0) {
            return true;
        }
        // 如果当前节点是叶子节点,但count不为0,则返回false
        if (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,则直接返回true
                return true;
            }
            // 否则,回溯,恢复count的值,继续处理右子树
            count += root->left->val;
        }
        // 如果右子节点存在
        if (root->right) {
            // 将count减去右子节点的值
            count -= root->right->val;
            // 递归调用traversal函数处理右子树
            if (traversal(root->right, count)) {
                // 如果右子树返回true,则直接返回true
                return true;
            }
            // 否则,回溯,恢复count的值
            count += root->right->val;
        }
        // 如果左子树和右子树都没有返回true,则返回false
        return 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) {
        // 如果根节点为空,则返回false
        if (!root) {
            return false;
        }
        // 如果根节点没有左子节点和右子节点,并且当前节点的值等于目标值,则返回true
        if (!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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt三方库:QuaZIP介绍、编译和使用

前言 Qt使用一些压缩解压功能&#xff0c;探讨过libzip库&#xff0c;zlib库&#xff0c;libzip库比较原始&#xff0c;还有其他库&#xff0c;都比较基础&#xff0c;而在基础库之上&#xff0c;又有高级封装库&#xff0c;Qt中的QuaZIP是一个很好的选择。Quazip是一个用于压缩…

Win11安装Docker Desktop运行Oracle 11g 【详细版】

oracle docker版本安装教程 步骤拉取镜像运行镜像进入数据库配置连接数据库&#xff0c;修改密码Navicat连接数据库 步骤 拉取镜像 docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g运行镜像 docker run -d -p 1521:1521 --name oracle11g registry.cn-ha…

MySQL的表级锁

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;面经 ⛺️稳中求进&#xff0c;晒太阳 表级锁 介绍 对于表锁&#xff0c;分为两类&#xff1a; 表共享读锁表独占写锁 语法 1. 加锁&#xff1a;lock tables 表名... read/write 2.…

Multitouch for Mac:手势自定义,提升工作效率

Multitouch for Mac作为一款触控板手势增强软件&#xff0c;其核心功能在于手势的自定义和与Mac系统的深度整合。通过Multitouch&#xff0c;用户可以轻松设置各种手势&#xff0c;如三指轻点、四指左右滑动等&#xff0c;来执行常见的任务&#xff0c;如打开应用、切换窗口、滚…

网络编程——Socket——模拟用户登录

功能一&#xff1a;模拟用户登录 功能二&#xff1a;实现客户发送登录用户信息&#xff0c;服务器端显示登录信息并响应给客户端登录成功 这里设置的用户登录信息为&#xff1a;admin&#xff0c;123456 实现&#xff1a; 1.首先&#xff0c;服务端创建并启动服务器&#x…

MyBatis——MyBatis入门程序

一、数据准备 二、开发步骤 1、引入依赖 <dependencies><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.15</version></dependency><dependency><groupId>c…

如何打开远程桌面连接?

远程桌面连接是一项强大的功能&#xff0c;它允许我们远程访问其他计算机&#xff0c;并在远程计算机上进行操作。这对于远程办公、技术支持和远程培训等场景非常有用。本文将介绍如何在不同操作系统中打开远程桌面连接。 Windows系统 在Windows操作系统中&#xff0c;打开远程…

算法练习day7

四数相加II 代码随想录 0454.四数相加II 454. 四数相加 II - 力扣&#xff08;LeetCode&#xff09; &#xff08;用时&#xff1a;0.5小时&#xff09; 思路 本道题是需要在四个数组中&#xff0c;各找一个数&#xff0c;这些数加起来能够等于0&#xff0c;那么就是答案元…

mysql基础概念

文章目录 登录mysqlmysql和mysqld数据库操作主流数据库MYSQL架构SQL分类 登录mysql 登录mysql连接服务器&#xff0c;mysql连接时可以指明主机用-h选项&#xff0c;然后就可以指定主机Ip地址&#xff0c;-P可以指定端口号 -u指定登录用户 -P指定登录密码 查看系统中有无mysql&…

【系统架构师】-选择题(十二)计算机网络

1、网闸的作用&#xff1a;实现内网与互联网通信&#xff0c;但内网与互联网不是直连的 2、管理距离是指一种路由协议的路由可信度。15表示该路由信息比较可靠 管理距离越小&#xff0c;它的优先级就越高&#xff0c;也就是可信度越高。 0是最可信赖的&#xff0c;而255则意味…

(Java)心得:LeetCode——11.盛最多水的容器

一、原题 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容…

【C++】CentOS环境搭建-安装CATCH2

【C】CentOS环境搭建-安装CATCH2 1.克隆Catch2仓库2. 进入Catch2目录3. 创建一个构建目录4. 使用CMake生成构建系统&#xff08;以及可能的编译&#xff09;5.安装Catch2&#xff08;可选&#xff0c;根据你的需求&#xff09; 1.克隆Catch2仓库 git clone https://github.com…

ansible部署lamp架构

搭建参考&#xff1a;ansible批量运维管理-CSDN博客 定义ansible主机清单 [rootansible-server ~]# vim /etc/hosts 192.168.200.129 host01 192.168.200.130 host02 [rootansible-server ~]# vim /etc/ansible/hosts [webserver] host01 host02 在ansible端编写index.html…

08.1.自定义图形

自定义图形 创建图形 随便选择几个参数直接添加 选择自定义折线图形查看

一键追爆款,GPT一键改文‌‍‬⁣⁡​⁤⁢​⁢⁡⁣‬‍‌​​‬ ​‍⁤‬ ‬⁡⁡⁡‍‌‬⁡⁡⁢‬⁤⁢⁢⁤​‍‌​​‬ ​⁣‌,绘唐3,绘唐工具

ai画影满足你的制作要求 一键追爆款&#xff0c;GPT一键改文 AI推文小说&漫画解说&解压混剪 人物定义&#xff0c;角色定义&#xff0c;lora转换&#xff0c;模型转换&#xff0c;可视化参考满足 一键追爆款 一键挂机生成&#xff0c;效果更精彩&#xff0c;使用更方…

苹果电脑怎么安装crossover 如何在Mac系统中安装CrossOver CrossOver Mac软件安装说明

很多Mac的新用户在使用电脑的过程中&#xff0c;常常会遇到很多应用软件不兼容的情况。加上自己以前一直都是用Windows系统&#xff0c;总觉得Mac系统用得很难上手。 其实&#xff0c;用户可以在Mac上安装CrossOver&#xff0c;它支持用户在Mac上运行Windows软件&#xff0c;例…

恶意软件正劫持安全软件更新进行分发

GuptiMiner 是一个高度复杂的威胁&#xff0c;最早在 2018 年发现&#xff0c;主要为了在大型企业中分发后门。一种是 PuTTY Link 的增强版本后门&#xff0c;能够针对本地网络进行 SMB 扫描&#xff0c;并通过网络横向移动到网络上其他可能易受攻击的 Windows 7 和 Windows Se…

如何给文件和文件夹添加备注信息

1. 给文件添加备注信息 1. 打开文件夹&#xff0c;点击查看 → 选项 → 更改文件夹和搜索选项 → 勾除隐藏受保护的操作系统文件 → 勾选显示隐藏的文件、文件夹和驱动器&#xff1b; 2. listary工具搜索desktop.ini&#xff0c;随便点击一个desktop.ini文件&#xff0c;即可…

线程同步--互斥锁,读写锁

线程同步 基本概念 线程的能力在于能够方便地通过全局变量或共享内存来交换信息&#xff0c;但这也带来了并发控制的复杂性&#xff0c;主要表现在如何安全地管理多个线程对共享资源的访问。这里涉及到几个关键的概念和技术&#xff1a; 临界区&#xff08;Critical Section…

走进C++:C到C++的过渡

目录 什么是C呢&#xff1f; C的发展史 多了一些吃前来很香的“语法糖”。 语法糖一&#xff1a;命名空间 命名空间有个强大的功能 如何使用 语法糖二&#xff1a;缺省参数 语法糖三&#xff1a;函数重载 语法糖四&#xff1a;引用 引用传参 引用返回 引用和…