代码随想录训练营 Day16打卡 二叉树 part04 513. 找树左下角的值 112. 路径总和 106. 从中序与后序遍历序列构造二叉树

news2024/9/23 11:24:39

代码随想录训练营 Day16打卡 二叉树 part04

一、 力扣513. 找树左下角的值

给定一个二叉树,判断它是否是 平衡二叉树
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
示例 :
输入: root = [2,1,3]
输出: 1

思路分析

  1. 确定递归函数的参数和返回值:

    参数:需要一个当前节点 root 和一个整数 depth 表示当前节点所在的深度。
    返回值:不需要返回值,因为我们只需要更新全局变量即可。

  2. 确定终止条件:

    当节点为空时,递归结束。
    当遇到叶子节点时,需要更新最大深度和对应的节点值。

  3. 确定单层递归的逻辑:

    更新最大深度和对应的节点值。
    递归遍历左右子树,并增加深度。

版本一 递归法 + 回溯

实现思路:使用递归+回溯方法遍历整棵树,通过比较深度找出最底层的最左边节点的值。递归中包括深度的增减来确保正确计算每个节点的深度。

class Solution:
    def findBottomLeftValue(self, root: TreeNode) -> int:
        self.max_depth = float('-inf')  # 初始化最大深度为负无穷大
        self.result = None  # 存放结果
        self.traversal(root, 0)  # 从根节点开始遍历
        return self.result  # 返回最底层最左边的节点值
    
    def traversal(self, node, depth):
        if not node.left and not node.right:
            # 如果是叶子节点
            if depth > self.max_depth:
                # 如果当前深度大于已记录的最大深度
                self.max_depth = depth  # 更新最大深度
                self.result = node.val  # 更新结果为当前叶子节点的值
            return
        
        if node.left:
            depth += 1  # 增加深度
            self.traversal(node.left, depth)  # 遍历左子树
            depth -= 1  # 回溯,恢复深度
        if node.right:
            depth += 1  # 增加深度
            self.traversal(node.right, depth)  # 遍历右子树
            depth -= 1  # 回溯,恢复深度

版本二 递归法+精简

实现思路:类似版本一,但是简化了代码,去除了不必要的深度回溯操作。直接在递归调用时增加深度,更为简洁。

class Solution:
    def findBottomLeftValue(self, root: TreeNode) -> int:
        self.max_depth = float('-inf')  # 初始化最大深度为负无穷大
        self.result = None  # 存放结果
        self.traversal(root, 0)  # 从根节点开始遍历
        return self.result  # 返回最底层最左边的节点值
    
    def traversal(self, node, depth):
        if not node.left and not node.right:
            # 如果是叶子节点
            if depth > self.max_depth:
                # 如果当前深度大于已记录的最大深度
                self.max_depth = depth  # 更新最大深度
                self.result = node.val  # 更新结果为当前叶子节点的值
            return
        
        if node.left:
            self.traversal(node.left, depth+1)  # 递归遍历左子树,直接增加深度
        if node.right:
            self.traversal(node.right, depth+1)  # 递归遍历右子树,直接增加深度

版本三 迭代法

实现思路:使用迭代的方式进行层序遍历(广度优先搜索),利用队列存储每一层的节点。记录每层的第一个节点的值,队列完成遍历后,最后记录的值即为最底层最左边的节点值。

from collections import deque
class Solution:
    def findBottomLeftValue(self, root):
        if root is None:
            return 0  # 如果根节点为空,则返回0
        queue = deque()  # 使用队列进行广度优先搜索
        queue.append(root)  # 加入根节点
        result = 0  # 初始化结果
        while queue:
            size = len(queue)  # 当前层的节点数
            for i in range(size):
                node = queue.popleft()  # 取出队列前端节点
                if i == 0:
                    result = node.val  # 记录每层的第一个节点值
                if node.left:
                    queue.append(node.left)  # 左子节点入队
                if node.right:
                    queue.append(node.right)  # 右子节点入队
        return result  # 循环结束时,result即为最底层最左边的节点值

力扣题目链接
题目文章讲解
题目视频讲解

二、 力扣112. 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
示例 :
在这里插入图片描述
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

图中可以看出,遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。
在这里插入图片描述

版本一 递归法

实现思路:这个版本通过递归遍历树的所有路径,并使用回溯来恢复计数状态。对于每个节点,若其为叶子节点且当前计数为0,则找到了有效路径。

class Solution:
    def traversal(self, cur: TreeNode, count: int) -> bool:
        if not cur.left and not cur.right and count == 0:  # 如果当前节点是叶子节点且计数为0
            return True  # 找到符合条件的路径
        if not cur.left and not cur.right:  # 如果是叶子节点但计数不为0
            return False  # 当前路径不符合条件
        
        if cur.left:  # 如果存在左子节点
            count -= cur.left.val  # 减去左子节点的值
            if self.traversal(cur.left, count):  # 递归检查左子树
                return True  # 如果找到符合条件的路径,返回True
            count += cur.left.val  # 回溯,恢复原来的计数值
            
        if cur.right:  # 如果存在右子节点
            count -= cur.right.val  # 减去右子节点的值
            if self.traversal(cur.right, count):  # 递归检查右子树
                return True  # 如果找到符合条件的路径,返回True
            count += cur.right.val  # 回溯,恢复原来的计数值
            
        return False  # 如果左右子树都没有找到符合条件的路径,返回False
    
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if root is None:
            return False  # 如果根节点为空,直接返回False
        return self.traversal(root, sum - root.val)  # 从根节点开始遍历,初始化计数为sum减去根节点的值

版本二 递归 + 精简

实现思路:这个版本简化了递归函数,去除了显式的回溯操作,直接在递归调用中更新sum。检查每个叶子节点时,如果剩余的sum等于节点的值,说明找到了一条有效路径。

class Solution:
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return False  # 如果节点为空,返回False
        if not root.left and not root.right and sum == root.val:
            return True  # 如果是叶子节点且剩余的sum等于节点值,返回True
        # 递归检查左右子树,更新sum为sum减去当前节点的值
        return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val)

版本三 迭代法

实现思路:使用栈来迭代地遍历树,检查每个叶子节点的路径和是否等于sum。这种方法不需要递归调用,使用显示的栈来保存每个节点的状态。

class Solution:
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return False  # 如果根节点为空,返回False
        st = [(root, root.val)]  # 使用栈,初始化包含根节点和其值
        while st:
            node, path_sum = st.pop()  # 弹出一个元素
            # 检查是否为叶子节点且路径和等于sum
            if not node.left and not node.right and path_sum == sum:
                return True  # 如果符合条件,返回True
            # 将子节点压栈,并更新路径和
            if node.right:
                st.append((node.right, path_sum + node.right.val))
            if node.left:
                st.append((node.left, path_sum + node.left.val))
        return False  # 如果栈为空也没有找到有效路径,返回False

力扣题目链接
题目文章讲解
题目视频讲解

三、 力扣106. 从中序与后序遍历序列构造二叉树

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 :
在这里插入图片描述
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

首先回忆一下如何根据两个顺序构造一个唯一的二叉树,相信理论知识大家应该都清楚,就是以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。
流程如图:

在这里插入图片描述
说到一层一层切割,就应该想到了递归。

来看一下一共分几步:

  • 第一步:如果数组大小为零的话,说明是空节点了。
  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  • 第五步:切割后序数组,切成后序左数组和后序右数组
  • 第六步:递归处理左区间和右区间

代码实现

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        # 特殊情况处理: 如果前序遍历结果为空,则二叉树也必然为空
        if not preorder:
            return None

        # 前序遍历的第一个元素为当前的根节点
        root_val = preorder[0]
        root = TreeNode(root_val)  # 创建根节点

        # 在中序遍历中找到根节点的位置,这个位置将数组分为左右两部分
        separator_idx = inorder.index(root_val)

        # 根据中序遍历中根节点的位置,切割中序数组,得到左子树和右子树的中序遍历结果
        inorder_left = inorder[:separator_idx]
        inorder_right = inorder[separator_idx + 1:]

        # 根据中序遍历中左子树的节点数量,切割前序数组,得到左子树和右子树的前序遍历结果
        # 前序遍历的左子树的长度与中序遍历的左子树长度相同
        preorder_left = preorder[1:1 + len(inorder_left)]
        preorder_right = preorder[1 + len(inorder_left):]

        # 递归构建左子树和右子树
        root.left = self.buildTree(preorder_left, inorder_left)
        root.right = self.buildTree(preorder_right, inorder_right)

        # 返回构建好的树的根节点
        return root

力扣题目链接
题目文章讲解
题目视频讲解

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

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

相关文章

探索算法系列 - 二分查找算法

目录 二分查找(原题链接) 在排序数组中查找元素的第一个和最后一个位置(原题链接) 搜索插入位置(原题链接) x 的平方根(原题链接) 山脉数组的峰顶索引(原题链接&…

数据结构(面试)

线索二叉树 原理:利用树节点的n1个左右空指针指向其遍历序列的前驱和后继(线索) 哈夫曼树 哈夫曼树定义:在含有n个带权叶节点的二叉树中,其中带权路径(WPL)最小的二叉树称为哈夫曼树&#x…

协程的八种创建方式

协程简介 在深入了解创建方式之前,我们先简要回顾一下协程是什么。协程是轻量级的线程。它们在协作式多任务处理中运行,允许在不阻塞线程的情况下挂起和恢复。这使得协程非常适合进行异步编程和高性能的并发任务。🌐 Kotlin中创建协程的方式…

IDA相关

IDA相关 IDA pro 7.7 链接: https://pan.baidu.com/s/14F0M1lxZMCoCAmR3AFmm-A?pwdjk14 提取码: jk14 lazy ida https://github.com/P4nda0s/LazyIDA pip install yara pip install keystone24.0.0.0 可以把ida目录里python site-package的内容复制到本机python的lib/si…

html写table表格,后端数据中涉及到身份证号或者电话号的情况,生成excel变成1+e17或者###等类似的加密或科学计数法情况

效果展示(对比图): 原: 新: 解决办法: 加以下样式即可展示原来的 style"vnd.ms-excel.numberformat:" 如若想知道…

链表的实现(C++版)

对于链表的学习,之前在C语言部分的时候就已经有学习过,也学会了使用C语言来打造一个链表.如今学了C 则想通过C来打造一个链表,以达到锻炼自己的目的. 1.链表的初步实现 1.节点模板的设置 template <class T> struct ListNode{ListNode <T>* _next;ListNode <T…

【Linux 网络】应用层

文章目录 应用层协议序列化的概念jsoncpp 1. HTTP1.1URLURL编解码 1.2 HTTP的格式HTTP请求格式HTTP响应格式 1.3 HTTP的方法GET/POST 1.4 HTTP的状态码1.5 HTTP的报头cookie和session1.6 简单HTTP服务器 2. HTTPS2.1 加密方式对称加密非对称加密全对称加密混合加密密钥协商对称…

CSS画箭头

向右示例 .arrows {height: 7px;width: 7px;background-color: transparent;border-top: 2px solid rgba(0, 0, 0, 0.3);border-right: 2px solid rgba(0, 0, 0, 0.3);transform: rotate(45deg);margin-left: 6px; } 可以尝试将其封装为组件&#xff08;以微信小程序为例&…

上位机《1》 步进电机、步进驱动器,连接端子(接线盒子)等

正运动技术 固高控制卡 雷赛控制卡 步进电机 电机内部的线圈数不同&#xff0c;组成的电机相数也不同&#xff0c;两相步进电机电机内部是由2个线圈组成&#xff0c;而三相步进电机内部是由3个线圈。 相数越多&#xff0c;步进角越小。所有精度就越高。步进电机转速越快&…

经验分享:大数据多头借贷风险对自身的不利影响?

在现代金融体系中&#xff0c;大数据技术的应用使得多头借贷成为一种普遍现象。多头借贷指的是个人或企业在短时间内同时或近期内申请多笔贷款或信用产品&#xff0c;这种行为可能带来一系列财务和信用风险。以下是大数据多头借贷风险对个人自身可能产生的不利影响&#xff1a;…

花了2小时,自己做了一个出入库系统

出入库管理是库存管理工作的重中之重&#xff0c;但在刚做产品出入库时&#xff0c;我可是踩了不少坑—— 库存管理不精确仓库作业效率低下货物追踪困难报表统计繁琐... 后来我就自学了下&#xff0c;花了两个小时自己做了一套织信出入库管理系统&#xff0c;全程没有敲一个代…

帆软10.0报表部署到正式环境后,不显示数据集的数据。

修改正式环境的数据连接 1&#xff09;数据连接的「编码」类型设置为「默认」。 2&#xff09;数据连接的「数据连接URL」后加后缀&#xff0c;如下图所示。格式为&#xff1a; jdbc:mysql://hostname:port/database?generateSimpleParameterMetadatatrue&useUnicodetru…

运维工程师,刚入职一个之前没有运维的公司,该做什么?

运维工程师&#xff0c;刚入职一个之前没有运维的公司&#xff0c;该做什么&#xff1f; 下面内容&#xff0c;只谈技术工作方面的&#xff0c;人情世故的方面自己体会吧。。。。 前言 写这个&#xff0c;主要是有一个朋友&#xff0c;也是运维工程师&#xff0c;五年以上运…

【STL】之 list 使用方法和模拟实现

目录 前言&#xff1a; list是什么&#xff1f; 节点类 迭代器类&#xff1a; list类 list的迭代器失效问题 前言&#xff1a; 之前我们分别手撕了string类和vector类&#xff0c;今天我们来跟list类打打交道~ list是什么&#xff1f; 通过查c文档可知&#xff0c;list…

(2)基于巴法云+MQTT+微信小程序控制esp8266点灯

目录 1、wifi配置指令表 2、连接连接wifi网络 3、连接巴法云MQTT &#xff08;1&#xff09;配置用户属性ATMQTTUSERCFG ① 命令格式&#xff1a; ② 命令参数&#xff1a; ③ 实际配置方式&#xff1a; &#xff08;2&#xff09;配置ESP 设备连接的 MQTT broker ① 命令格式…

【AI大模型】自动辅助驾驶的“大模型”时代

&#x1f388;边走、边悟&#x1f388;迟早会好 一、自动辅助驾驶实现与设计 1. 系统架构 1.1. 传感器系统 摄像头&#xff1a;提供前视、侧视、后视等多角度图像数据&#xff0c;用于检测车道线、交通标志、行人和其他车辆。雷达&#xff08;RADAR&#xff09;&#xff1a…

【python】PyQt5中QRadioButton的详细用法教程与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

论文速递 | Operations Research 6月文章合集

编者按&#xff1a; 在本系列文章中&#xff0c;我们梳理了运筹学顶刊Operations Research在2024年6月份发布的9篇相关文章的基本信息&#xff0c;旨在帮助读者快速洞察领域新动态。 推荐文章1 题目&#xff1a;Tight Guarantees for Multiunit Prophet Inequalities and On…

list模拟实现--用节点封装的迭代器问题

目录 一、list的使用 1.1list的构造 1.2list的iterator ​编辑 1.3 list的capacity 1.4 list的element access ​编辑 1.5list的mdifiers ​编辑 二、list的迭代器失效问题 三、list的模拟实现 3.1定义一个节点类 3.2用节点去封装迭代器 编译器对->的优化问题 …

JavaScript做网页是否过期的处理

通过路由上的参数生成唯一md5和路由上token做验证_md5 token-CSDN博客 前言&#xff1a;基于这篇文章我们做网页是否超时&#xff0c;网页是否过期的处理。打开一个网页允许他在一定时间内可以访问&#xff0c;过了这个时间就不可以访问了&#xff0c;encrypt是h5加密方法&…