算法打卡day16

news2024/12/24 2:07:38

今日任务:

1)513.找树左下角的值

2)112.路径总和

3)113.路径总和Ⅱ

4)106.从中序与后序遍历序列构造二叉树 

5)105.从前序与中序遍历序列构造二叉

513.找树左下角的值

题目链接:513. 找树左下角的值 - 力扣(LeetCode)

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。假设二叉树中至少有一个节点。


示例 1:
输入: root = [2,1,3]
输出: 1

示例 2:
输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

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

视频讲解:怎么找二叉树的左下角? 递归中又带回溯了,怎么办?| LeetCode:513.找二叉树左下角的值哔哩哔哩bilibili

层次遍历——思路:

首先最简单直接的就是采用层次遍历,收集每一层元素,最后返回最后一层的第一个元素result[-1][0]

稍微改进一下就是,遍历每一层时,只取队列的首位

# 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 findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0

        queue = collections.deque([root])
        result = []
        while queue:
            queue_size = len(queue)
            level = []
            for _ in range(queue_size):
                node = queue.popleft()
                level.append(node.val)
                if node.left:
                    queue.append(node.left)

                if node.right:
                    queue.append(node.right)
            result.append(level)

        return result[-1][0]

    # 改进
    def findBottomLeftValue2(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0

        queue = collections.deque([root])
        result = 0
        while queue:
            queue_size = len(queue)
            for i in range(queue_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

递归(DFS)——思路

核心是找到最后一行的最左侧节点,这个节点不一定是左节点。如果采用递归的话

1.我们要明确递归函数的参数和返回值

这里我们需要记录深度,所以传参除了常规的节点外,还需要传入深度,遍历过程中还需注意深度的回溯。整个过程中,我们需要有一个变量记录最大深度,一个变量记录最大深度第一次出现时叶子节点的值,这两个参数可以作为传参,也可以设为全局变量。

返回值则为最大深度第一次出现时的叶子节点

2.确定终止条件

当遇到叶子节点时,需要更新最大深度,如果此时深度比之前记录的最大深度大,那么同时也要更新记录返回值的变量

3.确定单层递归的逻辑

保证先左后右即可,这样第一次出现最大深度的节点一定在最左侧

class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        self.result = None
        self.maxDepth = float('-inf')
        self.getLeftNode(root, 0)
        return self.result


    def getLeftNode(self, node, depth):
        # 判断该节点是否为叶子节点,是则终止
        if node.left is None and node.right is None:

            if depth > self.maxDepth:
                self.result = node.val
                self.maxDepth = depth
            return

        if node.left:
            depth += 1
            self.getLeftNode(node.left, depth)
            depth -= 1

        if node.right:
            depth += 1
            self.getLeftNode(node.right, depth)
            depth -= 1

精简版:隐藏回溯

# 递归精简(隐藏回溯)
class Solution3:
    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)

112. 路径总和

题目链接:

112. 路径总和 - 力扣(LeetCode)

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

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

视频讲解:拿不准的遍历顺序,搞不清的回溯过程,我太难了! | LeetCode:112. 路径总和哔哩哔哩bilibili

112.路径总和——思路:

DFS深度优先遍历算法,前序遍历,每次遍历采用目标值去减当前节点,
一旦出现差值div为0,可以判断是否是叶子节点,是叶子节点返回true
如果遍历到叶子节点,div != 0 继续遍历
这题也要注意回溯过程

class Solution:
    def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
        if not root:
            return False
        div = targetSum - root.val
        return self.traversal(root, div)

    def traversal(self, node, div):
        # 终止条件
        if not node.left and not node.right:
            if div == 0:
                return True
            else:
                return False

        if node.left:
            div -= node.left.val
            if self.traversal(node.left, div):  # 左
                return True
            div += node.left.val  # 回溯

        if node.right:
            div -= node.right.val
            if self.traversal(node.right, div):  # 右
                return True
            div += node.right.val  # 回溯

        return False

精简写法,隐藏回溯

class Solution2:
    def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
        if not root:
            return False
        if not root.left and not root.right:
            if targetSum == root.val:
                return True
            else:
                return False

        return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)

113.路径总和 II 

题目链接:113. 路径总和 II - 力扣(LeetCode)

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例: 给定二叉树[5,4,8,11,None,13,4,7,2,None,None,5,1],以及目标和 sum = 22,
返回:[
    [5,4,11,2],
    [5,8,4,5]
   ]

 思路

DFS深度优先遍历算法,前序遍历,每次遍历采用目标值去减当前节点,
一旦出现差值div为0,可以判断是否是叶子节点,是叶子节点则保存当前节点
如果遍历到叶子节点,div != 0 继续遍历
这题也要注意回溯过程

class Solution:
    def PathSum(self, root: TreeNode, targetSum: int) -> list[list[int]]:
        self.result = []
        if not root:
            return []

        div = targetSum - root.val
        path = [root.val]
        self.traversal(root, path, div)
        return self.result

    def traversal(self, node, path, div):

        # 终止条件
        if not node.left and not node.right :
            if div == 0:
                self.result.append(path.copy())
            return

        if node.left:
            div -= node.left.val
            path.append(node.left.val)
            self.traversal(node.left, path, div) # 左
            div += node.left.val  # 回溯
            path.pop()

        if node.right:
            div -= node.right.val
            path.append(node.right.val)
            self.traversal(node.right, path, div) # 右
            div += node.right.val  # 回溯
            path.pop()

上面的代码中,有一个地方,要格外注意:当判断为叶子节点,且div为0时,我需要添加当前路径到列表中。

这里产生了一个问题,问题出现在将 path 添加到 self.result 中时,实际上添加的是 path 的引用而不是 path 的副本。这意味着当 path 发生变化时,已经添加到 self.result 中的路径也会随之变化。

解决这个问题的方法是,将 path 添加到 self.result 中时,添加 path 的副本而不是直接添加 path。这样就不会受到 path 变化的影响。我后来使用 path.copy() 来创建 path 的副本。也可以用path[:]

上面的代码好理解,但写的并不好,紧接进行改进  

1.避免全局变量:目前代码中使用了 self.result 作为全局变量来存储结果。虽然这种方法可以工作,但不推荐使用全局变量,因为它会增加代码的复杂性和维护成本。相反,可以将结果作为函数的返回值,这样可以使代码更加清晰和可维护。
2.递归中改成隐式回溯:在递归函数中不再修改传递的参数,而是在函数内部创建局部变量来处理路径和目标值。这样可以减少代码的复杂性,并降低出错的风险。
3.使用列表推导式简化代码:在遍历左右子树时,可以使用列表推导式来简化代码。这样可以使代码更加简洁和易读。

class Solution2:
    def PathSum(self, root: TreeNode, targetSum: int) -> list[list[int]]:
        result = []

        if not root:
            return result
        self.traversal(root, [root.val], targetSum - root.val, result)

        return result

    def traversal(self, node, path: list[int], target, result):
        # 终止条件
        if not node.left and not node.right and target == 0:
            result.append(path.copy())
            return

        # 左子树
        if node.left:
            self.traversal(node.left, path+[node.left.val], target - node.left.val, result)


        # 右子树
        if node.right:
            self.traversal(node.right, path+[node.right.val], target - node.right.val, result)

在上面代码中,我企图将路径path与差值div参数均隐藏回溯

div隐藏回溯没有什么问题,路径隐藏回溯时,我就很常规的将path.append(node.left.val)作为参数参入,存在一个潜在的问题。

在Python中,list.append() 方法会直接修改原列表,并且返回值为 None,因此我在递归调用时实际上传递的是 None,而不是修改后的 path。这可能导致程序运行时出现错误。

后来我使用 path + [node.left.val] 的方式创建新的路径,在递归调用时传递这个新的路径,而不是直接修改原路径 path。

106.从中序与后序遍历序列构造二叉树

题目链接:

106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

根据一棵树的中序遍历与后序遍历构造二叉树。
注意: 你可以假设树中没有重复的元素。
例如,给出中序遍历 inorder = [9,3,15,20,7]  后序遍历 postorder = [9,15,7,20,3]
返回二叉树[3,9,20,None,None,15,7]

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

视频讲解:坑很多!来看看你掉过几次坑 | LeetCode:106.从中序与后序遍历序列构造二叉树哔哩哔哩bilibili

思路:

中:左中右  后:左右中

核心:后序遍历列表中,最后一个肯定是中节点(也是根节点),通过这个中节点去拆分中序遍历列表的左右区间

1.后序数组为0,空节
2.后序数组最后一个元素为节点元素
3.寻找中序数组位置作切割点
4.切中序数组
5.切后序数组
6.递归处理中后序中的左右区间
7.返回答案

class Solution:
    def buildTree(self, inorder: list[int], postorder: list[int]) -> Optional[TreeNode]:
        # todo 1.后序数组为0,空节点
        if not postorder:
            return

        # todo 2.后序数组最后一个元素为节点元素
        node = TreeNode(postorder[-1])

        # todo 3.寻找中序数组位置作切割点
        index = inorder.index(node.val)

        # todo 4.切中序数组
        left_inorder = inorder[:index]
        right_inorder = inorder[index+1:]

        # todo 5.切后序数组
        size = len(left_inorder)
        left_postorder = postorder[:size]
        right_postorder = postorder[size:len(postorder)-1]

        # todo 6.递归处理中后序中的左右区间
        node.left = self.buildTree(left_inorder,left_postorder)
        node.right = self.buildTree(right_inorder,right_postorder)

        # todo 7.返回答案
        return node

105.从前序与中序遍历序列构造二叉树

题目链接:

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

思路:

中:左中右  前:中左右 
1.前序数组为0,空节点
2.前序数组第一个元素为节点元素
3.寻找中序数组位置作切割点
4.切中序数组
5.切前序数组
6.递归处理前中序中的左右区间
7.返回答案

class Solution:
    def buildTree(self, preorder: list[int], inorder: list[int]) -> Optional[TreeNode]:
        # todo 1.前序数组为0,空节点
        if not preorder:
            return

        # todo 2.前序数组第一个元素为节点元素
        node = TreeNode(preorder[0])

        # todo 3.寻找中序数组位置作切割点
        index = inorder.index(preorder[0])

        # todo 4.切中序数组
        left_inorder = inorder[:index]
        right_inorder = inorder[index+1:]

        # todo 5.切前序数组
        size = len(left_inorder)
        left_preorder = preorder[1:size+1]
        right_preorder = preorder[size+1:]

        # todo 6.递归处理前中序中的左右区间
        node.left = self.buildTree(left_preorder,left_inorder)
        node.right = self.buildTree(right_preorder,right_inorder)

        # todo 7.返回答案
        return node

感想:

这两题不好想,首先要知道前中后序遍历的顺序

前序:中左右     中序:左中右     后序:左右中

前中序与后中序都能确定唯一的二叉树,但是前后序不行

举个例子

tree1 的前序遍历是[1 2 3], 后序遍历是[3 2 1]。

tree2 的前序遍历是[1 2 3], 后序遍历是[3 2 1]。

那么tree1 和 tree2 的前序和后序完全相同,这是一棵树么,很明显是两棵树!

所以前序和后序不能唯一确定一棵二叉树

为什么非得需要中序呢

这也是解这题的核心。我们可以由前后序列表知道中间节点的位置,正是因为中序遍历是左中右,所以我们继而用这个中节点去拆分中序列表中左右子树区间,从而能进行递归。

如果我们拿到的是前后序,前序为中左右,后序为左右中国,及时知道中间节点也无法去拆分左右子树区间。所以我们能由前中序遍历或中后序遍历确定唯一的二叉树

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

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

相关文章

【Redis教程0x06】Redis持久化之RDB快照

引言 虽说 Redis 是内存数据库,但是它为数据的持久化提供了两个技术。由这两个技术构成了3种持久化方式: RDB快照(snapshotting)只追加文件(append-only file,AOF)RDB和AOF的混合持久化&#…

自锁电路设计

自锁电路设计 Hi,uu们,是不是经常要用到自锁电路,通常不是使用555芯片就是用比较器来做自锁,今天我们来简单看下自锁电路的设计.图1采用了比较器构建了一个自锁电路,采用低电平复位,当需要复位的时候志需要将反向端的二极管拉低一下即可,免去 三极管控制…

【Java基础知识总结 | 第六篇】Java反射知识总结

文章目录 6.Java反射知识总结6.1概述6.1.1什么是反射?6.1.2为什么使用反射? 6.2反射的原理6.3反射的使用6.3.1获取类对象(1)通过具体类的类名获取(2)通过对象实例获取(3)通过class.f…

(免费领源码)Java#SSM#MYSQL学生信息管理系统的设计与实现70168-计算机毕业设计项目选题推荐

摘 要 从20年代开始,计算机疯狂的出现在人们的生活以及工作当中,成为人们生活、工作的好帮手,计算机深入到每家每户当中,网络办公,网络教学更是替换了传统手工记录管理的方式,使用计算机办公可以不必局限于…

RocketMQ学习笔记:消息存储模型,持久化文件,过期文件删除

这是本人学习的总结,主要学习资料如下 马士兵教育rocketMq官方文档 目录 1、消息存储结构1.1、CommitLog详解1.1.1、CommitLog存储的优点 1.2、ConsumeQueue详解1.3、Index详解 2、持久化文件3、过期文件删除机制3.1、判断过期文件3.2、删除的时机 1、消息存储结构…

15、Spring Cloud Alibaba Sentinel实现熔断与限流

注:本篇文章主要参考周阳老师讲解的cloud进行整理的! 1、Sentinel 1.1、官网 https://sentinelguard.io/zh-cn/ 等价对标 Spring Cloud Circuit Breaker 1.2、是什么 https://github.com/alibaba/Sentinel/wiki 1.3、去哪下 https://github.com/alibab…

面向低成本巡线机器人的PID控制器优化——文末源码

目录 介绍 测试 电子元器件 系统特征 控制器设计 位置误差的计算 比例控制 积分控制 微分控制 改进的PID控制器 测试轨迹 源码链接 本文对经典PID控制器的改进和开环控制机制的发展进行了讨论,以提高差动轮式机器人的稳定性和鲁棒性。为了部署该算法&am…

Java学习Day3

一道简单练习题&#xff0c;对编号进行随机排名 import java.util.Random; import java.util.Scanner;public class Main {public static void rand(int[] a,int n){Random rnew Random();for (int i0;i<n;i){int rrr.nextInt(n);int tempa[i];a[i]a[rr];a[rr]temp;}}publ…

吴恩达机器学习-可选实验室:Softmax函数

文章目录 CostTensorflow稀疏类别交叉熵或类别交叉熵祝贺 在这个实验室里&#xff0c;我们将探索softmax函数。当解决多类分类问题时&#xff0c;该函数用于Softmax回归和神经网络。 import numpy as np import matplotlib.pyplot as plt plt.style.use(./deeplearning.mplstyl…

【Go】初始化项目

初始化 go mod init 项目的名字然后按照提示&#xff1a; 这一步会将依赖包安装上去 go mod tidy最后编译&#xff1a; go build *.go

Redis实战篇session共享问题

实战篇Redis 1.6、session共享问题 核心思路分析&#xff1a; 每个tomcat中都有一份属于自己的session,假设用户第一次访问第一台tomcat&#xff0c;并且把自己的信息存放到第一台服务器的session中&#xff0c;但是第二次这个用户访问到了第二台tomcat&#xff0c;那么在第…

抖店还好做吗?新人刚做有什么注意事项吗?2024抖店创业指南

我是王路飞。 “抖店还好做吗&#xff1f;”“现在入局抖店晚不晚&#xff1f;”“抖店还能做吗&#xff1f;” 这种累死的问题&#xff0c;在我20年开始做抖店的时候&#xff0c;就经常被人这样问。 一直到2024年的今天&#xff0c;依旧有很多人私信我&#xff0c;其中相当…

从数据页的角度看 B+ 树

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 大家背八股文的时候&#xff0c;都知道 MySQL 里 InnoDB 存储引擎是采用 B 树来组织数据的。 这点没错&#xff0c;但是大家知道 B 树里的节点里存放的是什么呢&#xff1f;查询数据的过程又是怎样的&am…

【进程概念】Linux进程状态 | 僵尸进程 | 孤儿进程

目录 Linux中的进程状态 R运行状态&S休眠状态 T/t停止状态stopped(tracing stop) D磁盘休眠状态Disk sleep X死亡状态dead&Z僵尸状态zombie 僵尸进程 僵尸进程的理解 演示僵尸进程 僵尸进程的危害 孤儿进程 孤儿进程的理解 演示孤儿进程 进程状态的查看…

简化业务流程,AppLink连接一定签

APPlink是什么 APPlink是RestCloud打造的一款简单易用的零代码自动化集成平台&#xff0c;为业务流程提供自动化的解决方案&#xff0c;将企业内部的核心系统以及第三方应用程序和云服务等进行集成。无论是开发人员还是业务人员&#xff0c;都可以使用APPlink轻松构建出高效、…

锁的7大分类

锁 首先会了解锁的整体概念&#xff0c;了解锁究竟有哪些分类的标准。在后面的文章中会对重要的锁进行详细的介绍。 锁的7大分类 需要首先指出的是&#xff0c;这些多种多样的分类&#xff0c;是评价一个事物的多种标准&#xff0c;比如评价一个城市&#xff0c;标准有人口多…

鸿蒙Harmony应用开发—ArkTS-高级组件:@ohos.advertising.AdComponent (非全屏广告展示组件))

本模块提供展示非全屏广告的能力。 说明&#xff1a; 本模块首批接口从API Version 11开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import { AdComponent } from ohos.advertising.AdComponent; AdComponent AdComponent(ads: Ar…

基于springboot+vue+Mysql的校园交友网站

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

基于Java校园跑腿管理系统设计与实现(源码+部署文档)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

OSCP靶场--Crane

OSCP靶场–Crane 考点(CVE-2022-23940sudo service提权) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.229.146 -sC -sV --min-rate 2500 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-25 08:07 EDT Nmap scan report for 192.16…