算法打卡day15

今日任务:

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.理解递归的返回值含义,确保递归函数的返回值是符合预期的。

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

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

相关文章

隐私计算实训营学习四:SecretFlow的安装和部署

文章目录 一、SecretFlow安装二、SecretFolw部署模式简介三、SecretFlow部署-仿真模式四、SecretFlow部署-生产模式 一、SecretFlow安装 SecretFlow运行要求: Python > 3.8操作系统:CentOS7、Anolis8、Ubuntu 18.04/20.04、macOS 11.1、WSL2资源&am…

共享打印机以及修复脱机状态打印机

title: 共享打印机以及修复脱机状态打印机 search: 2024-03-23 tags: “#共享打印机以及修复脱机状态打印机” 如何将打印机共享在局域网内 Tips:考虑将打印机共享,无非是要考虑两个问题,一个是将打印机作为外设的电脑怎么将打印机共享&…

随机密码生成器源码

源码简介 纯HTML,该去的已去掉,该简化的简化,最高支持32位混合随机密码生成 安装教程 纯HTML,直接将压缩包上传网站目录解压即可 首页截图 源码下载 随机密码生成器源码-小8源码屋源码简介 纯HTML,该去的已去掉&a…

阿里云4核16G服务器优惠价格26元1个月、149元半年

阿里云4核16G服务器优惠价格26.52元1个月、79.56元3个月、149.00元半年。2024年腾讯云服务器优惠价格表,一张表整理阿里云服务器最新报价,阿里云服务器网整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单,大家也…

Java学习笔记 | JavaSE基础语法05 | 方法

文章目录 0.前言1. 方法概述2. 方法的定义和调用2.1 无参数方法定义和调用2.2 带参数方法定义和调用1 带参数方法定义和调用2 形参和实参3 带参数方法练习 2.3 带返回值方法的定义和调用1 带返回值方法定义和调用2 带返回值方法练习13 带返回值方法练习24 带返回值方法练习3 3.…

STM32学习笔记(6_2)- TIM定时器中断和定时器内外时钟源选择代码

无人问津也好,技不如人也罢,都应静下心来,去做该做的事。 最近在学STM32,所以也开贴记录一下主要内容,省的过目即忘。视频教程为江科大(改名江协科技),网站jiangxiekeji.com 现在开…

浙大版《C语言程序设计(第4版)》题目集-习题3-5 三角形判断

给定平面上任意三个点的坐标(x1,y1)、(x2,y2)、(x3,y3),检验它们能否构成三角形。 输入格式: 输入在一行中顺序给出六个[−100,100]范围内的数字,即三个点的坐标x1、y1、x2、y2、x3、y3。 输出格式: 若这3个点不能构成三角形,则在一行中输…

移植 Zephyr 到 Art-Pi

背景 ​ 最近工作中接触到了 Zephyr,不由觉得 Zephyr 是个很强大、全面、优秀的实时操作系统,但同时是有一定的上手难度的,其复杂的构建系统让小编倒吸一口凉气。为了深入研究并完全掌控 Zephyr,小编决定把它移植到手头的开发板上…

[leetcode] 240. 搜索二维矩阵 II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1: 输入:matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,…

c高级 day3 shell脚本

1:输入数组,输出数组的所有元素,以及数组长度 37 read -a arr38 echo ${arr[*]}39 echo ${#arr[*]} 运行结果: 2:定义数组存储当前目录下的所有.sh文件,计算文件的个数 43 arr(ls *.sh)44 echo ${#arr…

大数据Spark--入门

文章目录 Spark 概述Spark 是什么Spark and HadoopSpark and HadoopSpark 核心模块 Spark 简单上手创建Maven项目增加 Scala 插件增加依赖关系WordCount异常处理 Spark 概述 Spark 所需资料 链接:https://pan.baidu.com/s/12iaW68vriL6i-xI1kmr0_g?pwdm4zc 提取码…

使能 Linux 内核自带的 FlexCAN 驱动

一. 简介 前面一篇文章学习了 ALPHA开发板修改CAN的设备树节点信息,并加载测试过设备树文件,文件如下: ALPHA开发板修改CAN的设备树节点信息-CSDN博客 本文是学习使能 IMX6ULL的 CAN驱动,也就是通过内核配置来实现。 二. 使能…

栅格地图路径规划:基于小龙虾优化算法(Crayfsh optimization algorithm,COA)的机器人路径规划(提供MATLAB代码)

一、机器人路径规划介绍 移动机器人(Mobile robot,MR)的路径规划是 移动机器人研究的重要分支之,是对其进行控制的基础。根据环境信息的已知程度不同,路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或…

目标检测上的diffusion

1 Title DiffusionDet: Diffusion Model for Object Detection(Shoufa Chen,Peize Sun,Yibing Song,Ping Luo)【ICCV 2023】 2 Conclusion This study proposes DiffusionDet, a new framework that formulates object detection as a denoisin…

Dynamo与Revit API之间的转换

今天来聊聊 Dynamo 与 Revit 之间图元转换的基础知识,这些需要你牢牢记住哦,不然在 Python script 中写代码,经常会报错的~ 通常来讲,所有来自 Dynamo 节点的几何图形都不是 Revit 的几何对象,所以它们需要与 Revit AP…

FreeCAD傻瓜教程之基准面的构建-在实体的表面上新建坐标、倾斜的平面、附加不同的台阶、旋转体等

目的:学会在已有模型的不同剖面上建立新的坐标系,并绘图;使得新图形仍然作为同一个零件实体的构件。 零、需求举例 在下列模型中,我们要在圆杆的顶部增加一个把手,如果点击圆杆顶部,则仅能在顶部圆形所在…

举4例说明Python如何使用正则表达式分割字符串

在Python中,你可以使用re模块的split()函数来根据正则表达式分割字符串。这个函数的工作原理类似于Python内置的str.split()方法,但它允许你使用正则表达式作为分隔符。 示例 1: 使用单个字符作为分隔符 假设你有一个由逗号分隔的字符串,你可…

设计模式之状态模式(一)

设计模式专栏: http://t.csdnimg.cn/4Mt4u 目录 1.概述 2.结构 3.实现 4.总结 1.概述 状态模式( State Pattern)也称为状态机模式( State Machine pattern), 是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类, 属于行为型模式。 在状…

Vscode按键占用问题解决

Vscode按键占用 在使用vscode的过程中,官方按键 Ctrl . 按键可以提示修复代码中的问题,但是发现按了没有反应。 解决问题 首先确认vscode中是否设置了这个按键,默认设置了的系统输入法中是否有按键冲突了,打开输入法设置检查 …

AI在融媒体领域的应用探讨

AI在融媒体领域的应用探讨 ChatGPT是人工智能的一种应用形式,它属于自然语言处理(NLP,Nature Language Process)领域。 2022年11月30日,由人工智能实验室OpenAI发布的对话式大型语言模型ChatGPT一夜爆火,…