算法打卡day15

news2024/11/15 19:50:39

今日任务:

1)110.平衡二叉树

2)257. 二叉树的所有路径

3)404.左叶子之和

110.平衡二叉树

题目链接:110. 平衡二叉树 - 力扣(LeetCode)

给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1

示例 1:
给定二叉树 [3,9,20,null,null,15,7]
输出:true

示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
输出:False

文章讲解:代码随想录 (programmercarl.com)

视频讲解:后序遍历求高度,高度判断是否平衡 | LeetCode:110.平衡二叉树哔哩哔哩bilibili

思路:

node的高度:node节点到最底层叶子节点的高度

这题弄清楚这个就比较好做了,采用递归法后序遍历(左右中)

1.确定递归函数的参数和返回值:将节点传入递归函数中,返回这个节点的高度

2.终止条件:递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0

3.单层递归逻辑:判断传入节点左右两个子树的高度差是否超过1

如果不超过1,说明是平衡的,则继续检查其左右子树是否也是平衡的。如果超过1,则不平衡,直接返回false。因为返回值为高度,这里为了返回值类型统一,我们可以将不平衡设为-1,如果返回值为-1,则表示不平衡;否则平衡,返回该节点高度

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


class Solution:
    def isBalanced(self, root: [TreeNode]) -> bool:
        if self.getHeight(root) != -1:
            return True
        else:
            return False

    def getHeight(self, node):
        if not node:
            return 0

        leftHeight = self.getHeight(node.left)  # 左
        rightHight = self.getHeight(node.right)  # 右

        if leftHeight == -1 or rightHight == -1:
            return -1  # 这里直接终止返回-1,不要在用height记录了,因为左右高度均为-1时,在后面判断中,两个相减是等于0的,会使height重新赋值为0
        else:
            height = max(leftHeight, rightHight) + 1  # 求出该节点高度

        # 如果左右子树高度差大于1,则用-1标记
        if abs(leftHeight - rightHight) > 1:
            height = -1
        # 如果之前没有返回-1,则表明该节点之前的子树都符合要求

        return height

感想:

避免重复计算: 为了避免重复计算,这题我们采用下而上的方式计算节点,也就是后序遍历,这样可以确保每个节点的高度只计算一次

适时剪枝: 在检查节点平衡性时,如果发现某个节点所在的子树已经不平衡,可以及早终止递归,提前返回结果,避免不必要的计算。

257. 二叉树的所有路径

题目链接:257. 二叉树的所有路径 - 力扣(LeetCode)

给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。

示例:
输入:[1,2,3,None,5,None,None]
输出:["1->2->5","1->3"]

文章讲解:代码随想录 (programmercarl.com)

视频讲解:递归中带着回溯,你感受到了没?| LeetCode:257. 二叉树的所有路径哔哩哔哩bilibili

思路:

当我们想要找到二叉树中所有从根节点到叶子节点的路径时,可以使用深度优先搜索(DFS)算法(前序遍历-->中左右)。我们可以从根节点开始,沿着每条路径向下遍历二叉树,直到到达叶子节点。在遍历过程中,我们将经过的节点值记录下来,直到到达叶子节点,然后将路径添加到结果中。

下面是具体的实现逻辑:

1.创建一个空列表 result 用于存储所有的路径。

2.编写一个递归函数 traversal,它接收当前节点 node 和当前路径 path 作为参数。

3.在递归函数中,首先将当前节点的值加入到路径 path 中。

4.判断当前节点是否为叶子节点(即没有左右子节点)。如果是叶子节点,则将当前路径 path 加入到结果列表 result 中。

5.如果当前节点不是叶子节点,则递归调用 traversal 函数分别遍历其左右子节点。在递归调用时,需要将当前路径 path 作为参数传递给下一级。

6.最后,在主函数中调用 traversal 函数,从根节点开始遍历二叉树,并将结果返回。

思路比较简单,但要注意里面的回溯过程

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def binaryTreePaths(self, root: [TreeNode]) -> list[str]:
        path = []
        result = []

        if not root:
            return result

        self.traversal(root, path, result)

        return result

    def traversal(self, node: [TreeNode], path: list[str], result: list[str]) -> None:
        # 将当前节点值加入路径中
        path.append(str(node.val))  # 中

        # 遇到叶子节点终止
        if not node.left and not node.right:
            sPath = '->'.join(path)
            result.append(sPath)
            return

        # 如果不是叶子节点,继续递归遍历左右子树
        if node.left:  # 左
            self.traversal(node.left, path, result)
            path.pop()  # 回溯

        if node.right:  # 右
            self.traversal(node.right, path, result)
            path.pop()  # 回溯

这一种是比较完整的写法,后面采用了隐藏回溯的方法。隐藏回溯方法核心是为了保证path的独立性,我们需要构建新的字符串传入,而不是path本身

第一种采用列表切片实现,相当于传入的path副本

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution2:
    def binaryTreePaths(self, root: [TreeNode]) -> list[str]:
        path = []
        result = []

        if not root:
            return result

        self.traversal(root, path, result)

        return result

    def traversal(self, node: [TreeNode], path:list[int], result: list[str]) -> None:
        path.append(node.val)
        if not node.left and not node.right:
            result.append('->'.join(map(str, path)))
        if node.left:
            self.traversal(node.left, path[:], result)
        if node.right:
            self.traversal(node.right, path[:], result)

在 Python 中,列表是可变对象,如果直接传递列表 path,则在递归调用过程中可能会修改当前路径列表,这会导致不正确的结果。因为在递归过程中,我们会多次修改 path,而不是每次递归调用都使用相同的初始路径。

所以,为了避免这种情况,我们需要在每次递归调用时传递当前路径的副本,而不是直接传递当前路径本身。通过使用切片 path[:],我们创建了当前路径的一个副本,并将副本传递给递归调用的下一级。这样,即使在递归调用中修改了副本 path,原始的当前路径列表 path 也不会受到影响。

简而言之,传递 path[:] 会创建当前路径的一个副本,这样可以确保每次递归调用使用的路径都是独立的,避免了在递归调用过程中修改原始路径的情况。

第二种采用字符串拼接实现

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


# 隐藏回溯版本(采用字符串拼接)
class Solution3:
    def binaryTreePaths(self, root: [TreeNode]) -> list[str]:
        path = ''
        result = []
        if not root: return result
        self.traversal(root, path, result)
        return result

    def traversal(self, cur: TreeNode, path: str, result: list[str]) -> None:
        path += str(cur.val)
        # 若当前节点为leave,直接输出
        if not cur.left and not cur.right:
            result.append(path)

        if cur.left:
            # + '->' 是隐藏回溯
            self.traversal(cur.left, path + '->', result)

        if cur.right:
            self.traversal(cur.right, path + '->', result)

在递归调用 traversal 方法时,我们传递的是 path + '->',而不是 path 本身。这里的 path + '->' 是一个新的字符串,它在每一次递归调用中都是独立的,不会修改原始的 path 字符串。

看整个代码,我们传入左右树的path依旧是当前递归中的path,并没有左节点递归的影响。通过这种方式,我们避免了在递归调用过程中修改原始的 path 字符串,从而确保了每次递归调用使用的路径都是独立的。

404.左叶子之和

题目链接:404. 左叶子之和 - 力扣(LeetCode)

给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。

示例:
输入:[1,2,3,None,5,None,None]
输出:["1->2->5","1->3"]

文章讲解:代码随想录 (programmercarl.com)

视频讲解:二叉树的题目中,总有一些规则让你找不到北 | LeetCode:404.左叶子之和哔哩哔哩bilibili

思路:

这题我们采用DFS中前序遍历(中左右)

递归遍历并计算左叶子节点的累加和:

我们需要一个递归函数来遍历二叉树并计算左叶子节点的累加和。这个递归函数将会是我们解决问题的核心。
在遍历的过程中,我们需要记录当前节点的值,并判断其是否为左叶子节点。
如果是左叶子节点,则将其值累加到一个变量中。
然后递归遍历左右子树,分别将左右子树的左叶子节点的值累加到上面的变量中。
返回累加和:

最后,我们返回累加和作为结果。

class Solution:
    def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:
        # 入口函数,调用递归函数
        return self.traversal(root)

    def traversal(self, node: Optional[TreeNode]) -> int:
        # 递归函数,计算左叶子节点的累加和
        if not node:
            return 0

        left_sum = 0  # 初始化左叶子节点的累加和为0

        if node.left and not node.left.left and not node.left.right:
            # 如果左子节点存在且为叶子节点,则累加其值到 left_sum 中
            left_sum += node.left.val

        # 递归遍历左右子树,将左右子树的左叶子节点值累加到 left_sum 中
        left_sum += self.traversal(node.left)
        left_sum += self.traversal(node.right)

        return left_sum  # 返回左叶子节点的累加和

感想:

这一题核心有几个要注意的地方

1.在递归函数中,确保对左右子树的遍历不会重复访问节点。

2.正确处理空节点的情况,避免出现空指针异常。

3.确保在每一级递归调用中,局部变量的值不会互相影响。

4.理解递归的返回值含义,确保递归函数的返回值是符合预期的。

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

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

相关文章

爬楼梯C语言

方法一&#xff1a;动态规划 int climbStairs(int n) {int f[100] {0};f[0] 0;f[1] 1;f[2] 2;for(int i 3;i<n;i)f[i] f[i-1] f[i-2];//可能是从i-1阶爬上第i阶&#xff0c;也有可能是从i-2阶 return f[n]; } 方法二&#xff1a;滚动数组 int climbStairs(int n){int…

DCDC60V80V100V转12V5V1A2A降压恒压芯片 惠海半导体原厂

H4020是一种内置40V耐压MOS&#xff0c;并且能够实现精确恒压以及恒流的同步降压型DC-DC转换器&#xff1b;支持1A持续输出电流输出电压可调&#xff0c;最大可支持 100%占空比&#xff1b;通过调节 FB 端口的分压电阻&#xff0c;可以输出 2.5V到 24V 的稳定电压 。H4020 具有…

【Science】:配位不饱和 Al3+ 中心作为 γ-Al2O3 上铂活性相催化剂的结合位点

在许多非均相催化剂中&#xff0c;金属颗粒与其氧化物载体的相互作用可以改变金属的电子属性&#xff0c;并且在确定颗粒形态和保持分散性方面发挥关键作用。我们结合使用了超高磁场、固态魔角旋转核磁共振光谱技术和高角环形暗场扫描透射电子显微镜技术&#xff0c;配合密度泛…

array go 语言的数组 /切片

内存地址通过& package mainimport "fmt"func main() {var arr [2][3]int16fmt.Println(arr)fmt.Printf("arr的地址是: %p \n", &arr)fmt.Printf("arr[0]的地址是 %p \n", &arr[0])fmt.Printf("arr[0][0]的地址是 %p \n"…

通过命令在Windows入站出站放行上放行端口8090, 8443, 5222, 8021

可以通过循环结构来简化操作&#xff0c;下面分别创建入站和出站规则的示例&#xff1a; 入站规则 $ports 8090, 8443, 5222, 8021foreach ($port in $ports) {New-NetFirewallRule -DisplayName "Allow Inbound Port $($port)" -Direction Inbound -Action Allow…

【协议-HTTP】

HTTP协议 HTTP协议(超文本传输协议HyperText Transfer Protocol)&#xff0c;它是基于TCP协议的应用层传输协议。http协议定义web客户端如何才能够web服务器请求web页面&#xff0c;以及服务器如何把web页面传送给客户端。 HTTP 是一种无状态 (stateless) 协议, HTTP协议本身…

基于前端技术实现的全面预算编制系统

前言 在现代商业环境中&#xff0c;预测销售数据和实际成本是每个公司CEO和领导都极为重视的关键指标。然而&#xff0c;由于市场的不断变化&#xff0c;准确地预测和管理这些数据变得愈发具有挑战性。为了应对这一挑战&#xff0c;建立一个高效的系统来管理和审查销售数据的重…

发电机回收公司哪家靠谱?二手房发电机回收公司排名榜

在当今这个能源日益紧张的时代&#xff0c;发电机的存在依然有着不可替代的重要地位。然而&#xff0c;随着科技的不断进步&#xff0c;老旧或退役的发电机往往会被淘汰。这时&#xff0c;选择一家可靠的发电机回收公司就显得尤为重要。本文将为您解析发电机回收行业的现状&…

曲线生成 | 图解Reeds-Shepp曲线生成原理(附ROS C++/Python/Matlab仿真)

目录 0 专栏介绍1 什么是Reeds-Shepp曲线&#xff1f;2 Reeds-Shepp曲线的运动模式3 Reeds-Shepp曲线算法原理3.1 坐标变换3.2 时间翻转(time-flip)3.3 反射变换(reflect)3.4 后向变换(backwards) 4 仿真实现4.1 ROS C实现4.2 Python实现4.3 Matlab实现 0 专栏介绍 &#x1f5…

Swift 从获取所有 NSObject 对象聊起:ObjC、汇编语言以及底层方法调用链(三)

概览 承接上一篇博文: Swift 从获取所有 NSObject 对象聊起:ObjC、汇编语言以及底层方法调用链(二)我们在其中讨论了如何使用第三方强大通用的钩子库 SwiftHook 来协助我们完成 NSObject 构造器 init 的 SWIZZ 操作。我们还讨论了为什么用 print 打印对象信息时会发生崩溃…

新手摄影笔记-基础知识-按键和参数说明【1】

1. 相机正反面 2.顶部 3.屏幕 4.光圈、快门、感光度 什么是景深呢&#xff1f;景深就是照片中清晰和模糊的范围&#xff0c;也就是前后的距离。景深越深&#xff0c;意味着照片中清晰的范围越大&#xff0c;前后的距离越长&#xff0c;背景越清晰。景深越浅&#xff0c;意味着照…

个人博客系列-后端项目-系统角色配置(8)

系统角色配置需要设置的接口 用户可以绑定多个角色&#xff0c;角色对应有多个路由权限。用户绑定角色后&#xff0c;可以访问当前角色下的各个api路由和菜单路由。 用户注册时设置用户角色修改用户角色&#xff08;同时对应用户可以访问的路由将会同步变更&#xff09;添加修…

vue3如何二次封装el-upload组件进行图片上传及删除

实现功能&#xff1a; 1、封装el-upload组件&#xff0c;父组件可以控制图片上传框的宽高 2、父组件可以传入提示信息&#xff0c;利用具名插槽 3、上传前的校验 4、实现删除功能 不同配置下的效果&#xff1a; 父组件引用&#xff1a; <script setup lang"ts"…

Linux的介绍以及其发展历史

文章目录 前言一、技术是推动社会发展的基本动力1.人为什么能成为万物之长呢&#xff1f;2.人为什么要发明工具&#xff0c;进行进化呢&#xff1f;3.人是如何发明工具的&#xff1f;4.为什么要有不同的岗位和行业&#xff1f; 二、计算机(操作系统)发展的基本脉络1.第一台计算…

酷炫的粒子动态表白HTML源码

源码介绍 酷炫的粒子动态表白HTML源码&#xff0c;自己自定义文字&#xff0c;动态组合文字&#xff0c;进行表白&#xff0c;喜欢的朋友可以下载使用&#xff0c;很不错的表白HTML代码 下载地址 酷炫的粒子动态表白HTML源码

Set A Light 3D Studio中文--- 打造专业级3D照明效果

Set A Light 3D Studio是一款专业的灯光模拟软件&#xff0c;专为摄影师和电影制片人打造。它允许用户在计算机上模拟并预览各种布光效果&#xff0c;助力拍摄出真实、精准且具有艺术感的作品。软件提供了丰富的灯光和场景模型&#xff0c;用户可以灵活调整光源参数&#xff0c…

优化金融展厅设计,细节提升客户体验与实用效能

“很赚钱”大部分公众对金融行业的第一印象&#xff0c;这足以见得金融行业在社会经济发展中的重要性&#xff0c;而为了更好的宣传和科普金融相关信息&#xff0c;金融展厅的设计和建设成为了重要措施&#xff0c;它能够充分展示金融机构的实力、品牌形象和服务优势&#xff0…

使用Django实现信号与消息通知系统【第154篇—Django】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 使用Django实现信号与消息通知系统 在Web应用程序中&#xff0c;实现消息通知系统是至关重…

【Hadoop大数据技术】——Hadoop高可用集群(学习笔记)

&#x1f4d6; 前言&#xff1a;Hadoop设计之初&#xff0c;在架构设计和应用性能方面存在很多不如人意的地方&#xff0c;如HDFS和YARN集群的主节点只能有一个&#xff0c;如果主节点宕机无法使用&#xff0c;那么将导致HDFS或YARN集群无法使用&#xff0c;针对上述问题&#…

网络协议栈--传输层--UDP/TCP协议

目录 本节重点一、再谈端口号1.1 再谈端口号1.2 端口号范围划分1.3 认识知名端口号(Well-Know Port Number)1.4 回答两个问题1.5 netstat1.6 pidof 二、UDP协议2.1 UDP协议段格式2.2 UDP的特点2.3 面向数据报2.4 UDP的缓冲区2.5 UDP使用注意事项2.6 基于UDP的应用层协议2.7 UDP…