力扣刷题-二叉树-路径总和

news2024/11/28 17:39:50

112 路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例: 给定如下二叉树,以及目标和 sum = 22,
image.png
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。

思路

参考:https://www.programmercarl.com/0112.%E8%B7%AF%E5%BE%84%E6%80%BB%E5%92%8C.html#%E6%80%9D%E8%B7%AF
递归函数什么时候要有返回值,什么时候没有返回值,特别是有的时候递归函数返回类型为bool类型。
那么接下来我通过详细讲解如下两道题,来回答这个问题:
112.路径总和
113.路径总和ii
这道题我们要遍历从根节点到叶子节点的路径看看总和是不是目标和。

递归

可以使用深度优先遍历的方式(本题前中后序都可以,无所谓,因为中节点也没有处理逻辑)来遍历二叉树

  1. 确定递归函数的参数和返回类型

参数:需要二叉树的节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。
再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:

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

而本题我们要找一条符合条件的路径,所以递归函数需要返回值,及时返回,那么返回类型是什么呢?
如图所示:
image.png
图中可以看出,遍历的路线,并不要遍历整棵树(只需要有符合题意得时候 立刻返回就行),所以递归函数需要返回值,可以用bool类型表示。
所以代码如下:

# 我的问题就是遍历到一条路径底部时候 如何转到另一条路径 —— > 回溯
    def travelsal(self, node, count): # 递归第一步 参数为当前节点 另外采用一个计数器 遍历路径时候减去当前遍历节点的值

在python中 函数头不明确指明返回值类型 但自己要知道 返回值什么 为什么需要返回值

  1. 确定终止条件

首先计数器如何统计这一条路径的和呢?
不要去累加然后判断是否等于目标和,那么代码比较麻烦,可以用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。(逆向思维)
如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。
如果遍历到了叶子节点,count不为0,就是没找到。
递归终止条件代码如下:

if not node.left and not node.right and count == 0:
    return True # 到达叶子节点 且count为0 说明当前路径刚好符合目标
if not node.left and not node.right:
    return False # 到达叶子节点 但是count不为0 因为路径有很多条 到达路径时候必然是叶子节点
  1. 确定单层递归的逻辑

因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。
递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回。
代码如下:

if node.left:
    count -= node.left.val
    if self.travelsal(node.left, count): # 递归处理节点
        return True
    count += node.left.val # 回溯 
# 右
if node.right:
    count -= node.right.val
    if self.travelsal(node.right, count):
        return True
    count += node.right.val # 回溯

完整代码:

class TreeNode(object):
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution(object):
    def hasPathSum(self, root, targetSum):
        """
        :type root: TreeNode
        :type targetSum: int
        :rtype: bool
        """
        if not root:
            return False
        # self.travelsal(root, targetSum)
        return self.travelsal(root, targetSum - root.val) # 减去根节点的 再遍历之后的

# 我的问题就是遍历到一条路径底部时候 如何转到另一条路径 —— > 回溯
    def travelsal(self, node, count): # 递归第一步 参数为当前节点 另外采用一个计数器 遍历路径时候减去当前遍历节点的值
        if not node.left and not node.right and count == 0:
            return True # 到达叶子节点 且count为0 说明当前路径刚好符合目标
        if not node.left and not node.right:
            return False # 到达叶子节点 但是count不为0 因为路径有很多条 到达路径时候必然是叶子节点
        # 左
        # if node.left:
        #     count -= node.left.val
        #     self.travelsal(node.left, count)
        #     count += node.left.val # 回溯 
        # 上面错误
        if node.left:
            count -= node.left.val
            if self.travelsal(node.left, count): # 递归处理节点
                return True
            count += node.left.val # 回溯 
        # 右
        if node.right:
            count -= node.right.val
            if self.travelsal(node.right, count):
                return True
            count += node.right.val # 回溯
        
        return False # 注意最后还要返回False 因为没有符合以上True的情况

迭代法

如果使用栈模拟递归的话,那么如果做回溯呢?
此时栈里一个元素不仅要记录该节点指针,还要记录从头结点到该节点的路径数值总和。

# 法二 迭代法
class Solution(object):
    def hasPathSum(self, root, targetSum):
        if not root:
            return False # 树为空 直接返回False
        # 采用前序遍历 中左右 
        stack = [(root, root.val)] # 栈里面存储的是节点及节点值
        while stack:
            node, path_sum = stack.pop()
            # 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
            if not node.left and not node.right and path_sum == targetSum:
                return True
            #  # 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
            if node.left:
                stack.append((node.left, path_sum + node.left.val))
            # 右节点
            if node.right:
                stack.append((node.right, path_sum + node.right.val))
        return False

113 路径总和2

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。(本题与上一题得区别是要找到所有的路径)
说明: 叶子节点是指没有子节点的节点。
示例: 给定如下二叉树,以及目标和 sum = 22,
image.png

思路

113.路径总和ii要遍历整个树,找到所有路径,所以递归函数不要返回值!
如图:
image.png
代码:

class TreeNode(object):
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

# 本题与112题不同的是 本题要找出所有路径 所以除了count计数之外 还需要一个列表来存储路径结果
class Solution(object):
    def __init__(self):
        self.result = [] # 总的结果
        self.path = [] # 单条路径结果

    def pathSum(self, root, targetSum):
        """
        :type root: TreeNode
        :type targetSum: int
        :rtype: List[List[int]]
        """
        # 先清空 result 和 path
        # self.result.clear()
        # self.path.clear()
        del self.result[:] # 注意若是 del self.result则是删除整个对象 而加了[:]是删除其中的元素
        del self.path[:]
        if not root:
            return self.result
        self.path.append(root.val) # 把根节点放入path
        self.travelsal(root, targetSum - root.val)
        return self.result
    
    def travelsal(self, node, count): # 用node表示更一般化 容易理解
        if not node.left and not node.right and count == 0:
            self.result.append(self.path[:]) # 收获结果 符合题目的单条路径加到总结果中 注意是 self.path[:]
            return 
        if not node.left and not node.right:
            return
        if node.left: # 左节点不为空 空节点不遍历
            self.path.append(node.left.val)
            count -= node.left.val
            self.travelsal(node.left, count) # 递归节点
            count += node.left.val # 回溯
            self.path.pop() # 回溯 pop不需要传入参数
        if node.right: # 右节点不为空 空节点不遍历
            self.path.append(node.right.val)
            count -= node.right.val
            self.travelsal(node.right, count) # 递归节点
            count += node.right.val # 回溯
            self.path.pop() # 回溯
        return # 不需要返回值 直接一个return就行

self.result.append(self.path) 与 self.result.append(self.path[:]) 的区别:

  1. result.append(self.path) 将 self.path 添加到 result 列表中,但实际上它是对 self.path 的引用。这意味着如果后续更改了 self.path 的值,result 列表中的相应元素也会受到影响,因为它们引用相同的对象。这是因为 self.path 是一个可变对象(例如,列表、字典),并且在列表中仅存储了对该对象的引用。
self.path = [1, 2, 3]
result = []
result.append(self.path)

self.path.append(4)
print(result)  # 输出 [1, 2, 3, 4]

  1. result.append(self.path[:]) 创造了 self.path 的一个副本,并将该副本添加到 result 列表中。这意味着即使后续更改了 self.path 的值,result 列表中的元素仍然保持不变,因为它们引用的是一个独立的副本。
self.path = [1, 2, 3]
result = []
result.append(self.path[:])

self.path.append(4)
print(result)  # 输出 [1, 2, 3]

总结

本篇通过leetcode上112. 路径总和 和 113. 路径总和ii 详细的讲解了 递归函数什么时候需要返回值,什么不需要返回值。
这两道题目是掌握这一知识点非常好的题目,大家看完本篇文章再去做题,就会感受到搜索整棵树和搜索某一路径的差别。
对于112. 路径总和,我依然给出了递归法和迭代法,这种题目其实用迭代法会复杂一些,能掌握递归方式就够了

参考:https://www.programmercarl.com/0112.%E8%B7%AF%E5%BE%84%E6%80%BB%E5%92%8C.html

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

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

相关文章

2024中国国际大数据产业博览会年度主题征集公告

2024中国国际大数据产业博览会年度主题征集公告 中国国际大数据产业博览会(以下简称数博会),是全球首个以大数据为主题的国家级博览会,由国家发展和改革委员会、工业和信息化部、国家互联网信息办公室和贵州省人民政府共同主办&am…

Android Studio好用的插件推荐

目录 一、插件推荐 二、如何下载 1.点击File—>Settings ​2.点击Plugins然后进行搜索下载 三、Android Studio 模板 一、插件推荐 这个插件可以为您自动生成Parcelable代码。Parcelable是一种用于在Android组件之间传递自定义对象的机制,但手动编写Parcela…

ffmpeg6.0-ffplay.c源码分析(一)之结构体、变量、宏详细解读

ffplay.c在全局定义的结构体、变量、宏就有300多行,去掉空格也有接近300行。 本文从程序相关变量、宏、结构体、全局变量四个方面来解读。 文章目录 程序相关变量宏结构体全局变量关注公众号看全文: 程序相关变量 和程序相关的变量只有两个: const char program_name[]…

常用模块之(time/datetime)

【 一 】时间模块(time/datetime) 【 二 】 表示时间的三种方式 *时间戳(Timestamp)是指1970年1月1日00:00:00开始计算的偏移量。可以使用time模块中的time()函数获取当前时间的时间戳,也可以使用datetime模块中的tim…

ArrayList vs. LinkedList: Java集合框架的比较与应用

目录 1. ArrayList简介 2. LinkedList简介 3. 内部实现方式 3.1 ArrayList的内部实现 3.2 LinkedList的内部实现 4. 时间复杂度比较 4.1 插入和删除操作 4.2 随机访问操作 5. 内存消耗 5.1 ArrayList的内存消耗 5.2 LinkedList的内存消耗 6. 适用场景 6.1 ArrayLi…

【小沐学Python】Python实现通信协议(grpc)

文章目录 1、简介2、安装3、定义接口4、编译生成5、代码编写5.1 服务端5.2 客户端5.3 运行测试 结语 1、简介 https://grpc.io/docs/what-is-grpc/introduction/ https://github.com/grpc/grpc gRPC 是一种现代、开源、高性能的远程过程调用 (RPC) 可以…

LRU 缓存机制_题解(一道经典的数据结构算法题)

LRU 缓存机制_题解(一道经典的数据结构算法题) 146. LRU 缓存 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int k…

ADB命令安装卸载手机APP

前言 手机内置的浏览器很多广告,推荐的新闻也很多负面的新闻,所以就想卸载内置的手机app,不过现在很多手机都是限制了内置的软件都不能卸载,以前随便获取一下root权限,也是可以卸载的,不过最近搞了一下&am…

基于YOLOv8深度学习的路面标志线检测与识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战

《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…

HarmonyOS开发实战:如何实现一个运动排名榜页面

HarmonyOS开发实战:如何实现一个运动排名榜页面 代码仓库: 运动排名榜页面 项目介绍 本项目使用声明式语法和组件化基础知识,搭建一个可刷新的排行榜页面。在排行榜页面中,使用循环渲染控制语法来实现列表数据渲染,…

PyCharm community 安装教程

目录 链接cudatoolkit 下载地址:https://www.jetbrains.com/pycharm/download/other.html 双击打开 安装路径,可自行更换到D盘 不导入设置 链接cudatoolkit

雪花算法(几种常见的雪花算法生成ID方案简单介绍:Hutool、百度Uid-Generator、美团Leaf、Yitter)

文章目录 1.生成id的几种方式2. 雪花算法2.1 雪花算法介绍2.2 市面上几种雪花算法的实现2.2.1 hutool版2.2.1.1 hutool版本雪花算法 关于时钟回拨的处理: ---------------百度UidGenerator 介绍开始--------------2.2.2 百度版:[UidGenerator](https://g…

【动态规划精选题目】2、路径问题模型

此动态规划系列主要讲解大约10个系列【后续持续更新】 本篇讲解路径问题模型中的6道经典题,会在讲解题目同时给出AC代码 目录 1、不同路径 2、不同路径2 3、珠宝的最大价值 4、下降路径最小和 5、最小路径和 6、地下城游戏 1、不同路径 class Solution { publi…

二叉树前,中序推后续_中,后续推前序

文章目录 介绍思路例子 介绍 二叉树是由根、左子树、右子树三部分组成。 二叉树的遍历方式又可以分为前序遍历,中序遍历,后序遍历。 前序遍历:根,左子树,右子树 中序遍历:左子树,根&#xff0…

Tor网络原理详解

引入 匿名通信是一种通过采用数据转发、内容加密、流量混淆等措施来隐藏通信内容及关系的隐私保护技术。为了提高通信的匿名性,这些数据转发链路通常由多跳加密代理服务节点构成,而所有这些节点即构成了匿名通信系统(或称匿名通信网络&#…

ReenterLock重入锁

synchronized就是一种最简单的控制方法,它决定了一个线程释放可以访问临界区资源。 同时,Object.wait()方法和Object.notify()方法起到了线程等待和通知的作用。 ReenterLock重入锁可以完全替代关键字Synchoronized.重入锁是Synchoronized、Object.wait(…

使用podman管理容器

目录 1.安装及配置podman 2.镜像的命名 3.对镜像重新做标签 4.删除镜像 5.查看镜像的层结构 6.导出和导入镜像 7.创建容器 8.创建一个简单的容器 9.容器的生命周期 10.创建临时容器 11.指定容器中运行的命令 12.创建容器时使用变量 对于初学者来说,不太容易理…

深入解析HashMap数据结构及其应用

目录 引言 1. HashMap简介 2. 哈希表的基本原理 3. HashMap的内部结构 4. 哈希冲突的处理 5. HashMap的常见操作 6. HashMap的性能优化 7. 实际应用场景 结论 引言 在计算机科学中,数据结构是构建和组织数据的一种方式,而HashMap是其中一种常用…

TCP单人聊天

TCP和UDP两种通信方式它们都有着自己的优点和缺点 这两种通讯方式不通的地方就是TCP是一对一通信 UDP是一对多的通信方式 TCP通信 TCP通信方式呢 主要的通讯方式是一对一的通讯方式,也有着优点和缺点 它的优点对比于UDP来说就是可靠一点 因为它的通讯方式是需…

ripro后台登录后转圈和图标不显示的原因及解决方法

最近,好多小伙伴使用ripro主题的小伙伴们都发现,登录后台后,进入主题设置就转圈,等待老半天后好不容易显示页面了,却发现图标不显示了,都统一显示为方框。 这是因为后台的js、css这类静态资源托管用的是js…