代码随想录训练营 Day17打卡 二叉树 part05 654. 最大二叉树 617. 合并二叉树 700. 二叉搜索树中的搜索 98. 验证二叉搜索树

news2024/11/23 11:27:01

代码随想录训练营 Day17打卡 二叉树 part05

一、 力扣654. 最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
  创建一个根节点,其值为 nums 中的最大值。
  递归地在最大值 左边子数组前缀上 构建左子树。
  递归地在最大值 右边子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树
示例 :
在这里插入图片描述
输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释
递归调用如下所示:

  • [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
    - [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
         - 空数组,无子节点。
         - [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
         - 空数组,无子节点。
         - 只有一个元素,所以子节点是一个值为 1 的节点。
    - [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
         - 只有一个元素,所以子节点是一个值为 0 的节点。
         - 空数组,无子节点。

构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。最大二叉树的构建过程如下:
在这里插入图片描述

版本一 基础版

实现思路:这个方法遍历数组找到最大值和它的索引,然后递归地使用最大值左侧和右侧的子数组来构建当前节点的左子树和右子树。

class Solution:
    def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
        if len(nums) == 1:
            return TreeNode(nums[0])  # 如果数组长度为1,直接返回该元素构成的节点
        
        node = TreeNode(0)  # 初始化节点
        # 找到数组中最大的值和对应的下标
        maxValue = 0
        maxValueIndex = 0
        for i in range(len(nums)):
            if nums[i] > maxValue:
                maxValue = nums[i]
                maxValueIndex = i
        node.val = maxValue  # 设置节点值为最大值
        
        # 最大值左边的数组构造左子树
        if maxValueIndex > 0:
            node.left = self.constructMaximumBinaryTree(nums[:maxValueIndex])
        
        # 最大值右边的数组构造右子树
        if maxValueIndex < len(nums) - 1:
            node.right = self.constructMaximumBinaryTree(nums[maxValueIndex + 1:])
        
        return node

版本二 使用下标

实现思路:此版本优化了空间使用,避免了数组切片,而是通过索引来操作。递归函数处理从left到right的子数组,寻找最大值并递归构建子树。

class Solution:
    def traversal(self, nums: List[int], left: int, right: int) -> TreeNode:
        if left >= right:
            return None  # 如果当前子数组为空,返回None
        
        maxValueIndex = left
        for i in range(left + 1, right):
            if nums[i] > nums[maxValueIndex]:
                maxValueIndex = i
        root = TreeNode(nums[maxValueIndex])  # 创建根节点为最大值
        
        # 递归创建左右子树
        root.left = self.traversal(nums, left, maxValueIndex)
        root.right = self.traversal(nums, maxValueIndex + 1, right)
        
        return root

    def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
        return self.traversal(nums, 0, len(nums))

版本三 使用切片

实现思路:此版本直接使用Python的切片功能和内置函数来简化代码。它在每次递归中通过max函数和index方法找到最大值及其索引,然后分别对左右子数组进行递归,构建左右子树。

class Solution:
    def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
        if not nums:
            return None  # 如果数组为空,返回None
        
        max_val = max(nums)  # 找到数组中的最大值
        max_index = nums.index(max_val)  # 找到最大值的索引
        node = TreeNode(max_val)  # 创建节点
        
        # 使用切片递归构建左右子树
        node.left = self.constructMaximumBinaryTree(nums[:max_index])
        node.right = self.constructMaximumBinaryTree(nums[max_index + 1:])
        
        return node

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

二、 力扣617. 合并二叉树

给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
示例 :在这里插入图片描述
输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]

本题使用哪种遍历都是可以的!我们下面以前序遍历为例。
动画如下:
在这里插入图片描述

版本一 递归 - 前序 - 修改root1

实现思路:

  • 递归地合并两棵树。
  • 在每个节点上,将 root2 的节点值加到 root1 的相应节点上。
  • 递归合并每个节点的左子树和右子树。
  • 返回修改后的 root1 作为结果。
class Solution:
    def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
        # 如果其中一个树为空,直接返回另一个树作为结果
        if not root1:
            return root2
        if not root2:
            return root1
        
        # 将root2的值加到root1上,这样就可以避免创建新节点,节约空间
        root1.val += root2.val
        
        # 递归合并左子树
        root1.left = self.mergeTrees(root1.left, root2.left)
        # 递归合并右子树
        root1.right = self.mergeTrees(root1.right, root2.right)
        
        # 返回修改后的root1作为合并后的树的根节点
        return root1

版本二 递归 - 前序 - 新建root

实现思路:

  • 与版本一类似,但不修改原始树的节点,而是为每个合并后的节点创建新的节点。
  • 适用于情况中要求不更改原有树的结构。
class Solution:
    def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
        # 特殊情况处理
        if not root1:
            return root2
        if not root2:
            return root1
        
        # 创建新节点作为合并后的新树的根节点
        root = TreeNode(root1.val + root2.val)
        
        # 递归创建左右子树
        root.left = self.mergeTrees(root1.left, root2.left)
        root.right = self.mergeTrees(root1.right, root2.right)
        
        return root

版本三 迭代

实现思路:

  • 使用队列进行层次遍历。
  • 逐对节点进行合并。
  • 如果两个节点都有子节点,将它们加入队列继续合并。
  • 如果某个节点的子节点为空,直接取另一个节点的子节点。
from collections import deque

class Solution:
    def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
        # 如果root1为空,返回root2作为合并结果
        if not root1:
            return root2
        # 如果root2为空,返回root1作为合并结果
        if not root2:
            return root1

        # 初始化队列,将两棵树的根节点加入队列
        queue = deque()
        queue.append(root1)
        queue.append(root2)

        # 当队列不为空,继续执行循环
        while queue:
            # 弹出两个节点进行合并
            node1 = queue.popleft()
            node2 = queue.popleft()
            
            # 将node2的值加到node1上,实现节点的合并
            node1.val += node2.val
            
            # 如果两个节点的左子树都存在,将它们的左子节点分别加入队列
            if node1.left and node2.left:
                queue.append(node1.left)
                queue.append(node2.left)
            # 如果node1的左子树不存在,直接将node2的左子树挂到node1上
            elif not node1.left:
                node1.left = node2.left
            
            # 同理,处理右子树
            if node1.right and node2.right:
                queue.append(node1.right)
                queue.append(node2.right)
            # 如果node1的右子树不存在,直接将node2的右子树挂到node1上
            elif not node1.right:
                node1.right = node2.right

        # 返回合并后的树的根节点
        return root1

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

三、 力扣700. 二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
示例 :
在这里插入图片描述
输入:root = [4,2,7,1,3], val = 2
输出:[2,1,3]

二叉搜索树是一个有序树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉搜索树

这就决定了,二叉搜索树,递归遍历和迭代遍历和普通二叉树都不一样。本题,其实就是在二叉搜索树中搜索一个节点。

对于二叉搜索树,不需要回溯的过程,因为节点的有序性就帮我们确定了搜索的方向。例如要搜索元素为3的节点,我们不需要搜索其他节点,也不需要做回溯,查找的路径已经规划好了。中间节点如果大于3就向左走,如果小于3就向右走,如图:
在这里插入图片描述

版本一 递归

实现思路:

  • 使用递归方法,从根节点开始。
  • 每次根据当前节点值与目标值的比较,决定是进入左子树还是右子树进行搜索。
  • 当找到目标节点或到达叶子节点但未找到目标时停止搜索。
class Solution:
    def searchBST(self, root: TreeNode, val: int) -> TreeNode:
        # 如果节点为空或者节点的值等于搜索值,则返回该节点
        if not root or root.val == val: 
            return root

        # 如果当前节点的值大于搜索值,搜索左子树
        if root.val > val: 
            return self.searchBST(root.left, val)

        # 如果当前节点的值小于搜索值,搜索右子树
        if root.val < val: 
            return self.searchBST(root.right, val)

版本二 迭代

实现思路:

  • 不使用递归,而是使用迭代的方式。
  • 通过循环和条件判断,逐步向下移动到合适的子节点。
  • 当找到值匹配的节点或者无法再向下移动时(即root变为None),循环结束。
class Solution:
    def searchBST(self, root: TreeNode, val: int) -> TreeNode:
        # 使用循环,从根节点开始遍历树
        while root:
            # 如果当前节点的值大于目标值,移动到左子节点
            if val < root.val: 
                root = root.left
            # 如果当前节点的值小于目标值,移动到右子节点
            elif val > root.val: 
                root = root.right
            # 如果相等,找到了目标节点,返回该节点
            else: 
                return root
        # 如果遍历完整棵树都没有找到,返回None
        return None

版本三 栈-遍历

实现思路:

  • 使用栈来进行深度优先搜索。
  • 不同于方法二的是,这里不区分左右子节点的压栈顺序,而是同时考虑两个方向。
  • 每次从栈中取出一个节点,检查它的值是否匹配,如果不匹配,则将其子节点压栈继续搜索。
class Solution:
    def searchBST(self, root: TreeNode, val: int) -> TreeNode:
        stack = [root]  # 初始化栈,并将根节点压入栈
        while stack:
            node = stack.pop()  # 弹出栈顶元素
            if node.val == val:  # 如果节点的值等于搜索值,返回该节点
                return node
            # 如果当前节点有右子节点,将右子节点压入栈
            if node.right:
                stack.append(node.right)
            # 如果当前节点有左子节点,将左子节点压入栈
            if node.left:
                stack.append(node.left)
        return None  # 如果栈空了还没找到,返回None

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

四、 力扣98. 验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
  节点的左子树只包含 小于 当前节点的数。
  节点的右子树只包含 大于 当前节点的数。
  所有左子树和右子树自身必须也是二叉搜索树。
示例 :
在这里插入图片描述
输入:root = [2,1,3]
输出:true

版本一 递归法 利用中序递增性质,转换成数组

实现思路:

  • 使用中序遍历收集树中的所有节点值。
  • 因为对于二叉搜索树,中序遍历结果应该是严格递增的。
  • 通过比较相邻元素确保整个数组是递增的。
class Solution:
    def __init__(self):
        self.vec = []  # 初始化空数组用于存储中序遍历的结果

    def traversal(self, root):
        if root is None:
            return
        self.traversal(root.left)  # 访问左子树
        self.vec.append(root.val)  # 将节点值加入数组
        self.traversal(root.right)  # 访问右子树

    def isValidBST(self, root):
        self.vec = []  # 清空数组以备新的验证
        self.traversal(root)  # 执行中序遍历
        # 检查数组是否严格递增
        for i in range(1, len(self.vec)):
            if self.vec[i] <= self.vec[i - 1]:
                return False
        return True

版本二 递归法 设定极小值,进行比较

实现思路:

  • 使用中序遍历的过程中,利用一个变量记录前一个节点的值。
  • 确保每个节点的值都大于之前的值。
  • 这种方法避免了数组存储,空间复杂度更优。
class Solution:
    def __init__(self):
        self.maxVal = float('-inf')  # 初始化为负无穷大

    def isValidBST(self, root):
        if root is None:
            return True
        # 先递归检查左子树
        left = self.isValidBST(root.left)
        # 检查当前节点的值是否大于前一个节点的值
        if self.maxVal < root.val:
            self.maxVal = root.val  # 更新为当前节点的值
        else:
            return False
        # 再递归检查右子树
        right = self.isValidBST(root.right)
        return left and right

版本三 递归法 直接取该树的最小值

实现思路:

类似于版本二,但在这里使用self.pre直接引用前一个节点,而不是其值。
这样可以直接比较节点的值而无需额外的值传递。

class Solution:
    def __init__(self):
        self.pre = None  # 用于记录前一个节点

    def isValidBST(self, root):
        if root is None:
            return True
        # 验证左子树
        left = self.isValidBST(root.left)
        # 验证当前节点
        if self.pre is not None and self.pre.val >= root.val:
            return False
        self.pre = root  # 更新前一个节点
        # 验证右子树
        right = self.isValidBST(root.right)
        return left and right

版本四 迭代法

实现思路:

  • 使用栈进行中序遍历。
  • 在遍历过程中比较当前节点与前一个访问的节点的值。
  • 保证每次访问的节点的值都必须大于前一个节点的值。
class Solution:
    def isValidBST(self, root):
        stack = []
        cur = root
        pre = None  # 记录前一个节点
        while cur is not None or stack:
            while cur:
                stack.append(cur)
                cur = cur.left  # 深入访问左子树
            cur = stack.pop()  # 访问节点
            if pre is not None and cur.val <= pre.val:
                return False
            pre = cur  # 更新前一个节点
            cur = cur.right  # 进入右子树
        return True

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

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

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

相关文章

基于Raft算法的分布式KV数据库:一、开篇

项目描述&#xff1a;本项目是基于Raft算法的分布式KV数据库&#xff0c;保证了分布式系统的数据一致性和分区容错性&#xff0c;在少于半数节点发生故障时仍可对外提供服务。使用个人实现的分布式通信框架mpRPC和跳表数据库skipList提供RPC服务和KV存储服务。 github地址&…

如何在数据埋点中发现和修复数据上报逻辑错误

如何发现和处理数据埋点中的逻辑错误 在大数据分析中,数据埋点是至关重要的一环。然而,当我们遇到数据上报逻辑错误时,该如何应对呢?本文将为你揭示解决这一棘手问题的有效方法。 目录 如何发现和处理数据埋点中的逻辑错误什么是数据上报逻辑错误?如何发现数据上报逻辑错误…

Python酷库之旅-第三方库Pandas(060)

目录 一、用法精讲 231、pandas.Series.reorder_levels方法 231-1、语法 231-2、参数 231-3、功能 231-4、返回值 231-5、说明 231-6、用法 231-6-1、数据准备 231-6-2、代码示例 231-6-3、结果输出 232、pandas.Series.sort_values方法 232-1、语法 232-2、参数…

Flink-StarRocks详解:第三部分StarRocks分区分桶(第53天)

文章目录 前言2.3 数据分布2.3.1 数据分布概览2.3.1.1 常见的数据分布方式2.3.1.2 StarRocks的数据分布方式2.3.1.3 分区2.3.1.4 分桶 2.3.2 创建分区2.3.2.1 表达式分区2.3.2.1.1 时间函数表达式分区&#xff08;自v3.1&#xff09;2.3.2.1.2 列表达式分区&#xff08;自v3.1&…

EAK水冷电阻60kW负载制动电阻器

描述 EAK制动电阻器 液冷电阻器将多余的制动能量转化为有用的热量&#xff0c;因此非常适合电动或混合动力驱动。与传统的风冷制动电阻器相比&#xff0c;水冷可节省高达 88% 的额外空间。作为一个额外的功能&#xff0c;电阻器可以很容易地与无滴漏的快速紧固件连接。由于即使…

注意力特征融合

摘要 https://arxiv.org/pdf/2009.14082 特征融合&#xff0c;即来自不同层或分支的特征的组合&#xff0c;是现代网络架构中无处不在的一部分。它通常通过简单的操作来实现&#xff0c;如求和或拼接&#xff0c;但这可能不是最佳选择。在这项工作中&#xff0c;我们提出了一种…

【Mybatis】浅谈Mybatis的缓存机制,一级缓存和二级缓存

目录 1. 缓存机制介绍 2. 一级缓存 3. 二级缓存 4. mybatis缓存执行流程 1. 缓存机制介绍 MyBatis 的缓存机制是为了提高应用程序的性能而设计的&#xff0c;通过缓存策略来减少数据库的查询次数。MyBatis 提供了两种类型的缓存&#xff1a;一级缓存和二级缓存。 默认情况下…

web框架:Django进阶(二)

文章目录 Django进阶&#xff08;二&#xff09;1.orm1.1 基本操作1.2 连接数据库1.3 连接池1.4 多数据库1.4.1 读写分离1.4.2 分库&#xff08;多个app ->多数据库&#xff09;1.4.3 分库&#xff08;单app&#xff09;1.4.4 注意事项 1.5 表关系1.6 数据操作单表一对多正向…

IIS解析漏洞~ IIS7.漏洞分析

IIS解析漏洞 文件解析漏洞是由于中间件错误的将特殊格式的文件解析成可执行网页文件(脚本)&#xff0c;配合文件上传漏洞进行GetShell的漏洞&#xff01; 1.2&#xff1a;IIS7.X 在IIS7.0和IIS7.5版本下也存在解析漏洞&#xff0c;在默认Fast-CGI开启状况下&#xff0c;在一个文…

Modbus通讯协议

Modbus通讯协议 Modbus协议是一种用于电子控制器之间的通信协议&#xff0c;‌它允许不同类型的设备之间进行通信&#xff0c;‌以便进行数据交换和控制。‌Modbus协议最初为可编程逻辑控制器&#xff08;‌PLC&#xff09;‌通信开发&#xff0c;‌现已广泛应用于工业自动化领…

Error: No module factory available for dependency type: CssDependency

本篇主要用来记录VUE打包的问题点&#xff0c;今天使用npm run build:prod 打包VUE出现如下问题&#xff1a; Error: No module factory available for dependency type: CssDependency 因为测试和预发布都挺正常的&#xff0c;正式环境竟然出问题&#xff0c;废话不多说&…

用 Python 编写的井字游戏

一.介绍 在本文中&#xff0c;我将向您展示如何使用 Python 创建一个非常简单的井字游戏。 井字游戏是一种非常简单的双人游戏。因此每次只能有两个玩家玩。该游戏也称为井字游戏或 Xs 和 Os 游戏。一个玩家玩 X&#xff0c;另一个玩家玩 O。在这个游戏中&#xff0c;我们有一…

java+springboot+mysql疾病查询网站01548-计算机毕业设计项目选题推荐(附源码)

摘 要 随着互联网时代的到来&#xff0c;同时计算机网络技术高速发展&#xff0c;网络管理运用也变得越来越广泛。因此&#xff0c;建立一个B/S结构的疾病查询网站&#xff0c;会使疾病查询工作系统化、规范化&#xff0c;也会提高医院形象&#xff0c;提高管理效率。 本疾病查…

最强开源模型 Llama 3.1 部署推理微调实战大全

目录 引言一、Llama 3.1简介二、Llama 3.1性能评估三、Llama 3.1模型推理实战1、环境准备2、安装依赖3、模型下载4、模型推理 四、Llama 3.1模型微调实战1、数据集准备2、导入依赖包3、读取数据集4、处理数据集5、定义模型6、Lora配置7、配置训练参数8、开始Trainer训练9、合并…

Effective Java 学习笔记--第18、19条继承与复合

目录 继承的设计 对用于继承的类可覆盖方法的说明 被继承类还需要遵循的约束 如何对继承类进行测试 如何禁止继承 复合的设计 什么是复合 复合的缺点 这两条的关系较强&#xff0c;核心都是继承&#xff0c;但是更强调继承的脆弱性&#xff0c;而且给出了继承的一个更优…

【云原生】Helm来管理Kubernetes集群的详细使用方法与综合应用实战

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

香港电讯亮相2024算网融合产业发展大会,荣获“SD-WAN优秀产品奖”

秉承“开放、创新、融合、共赢”的发展战略&#xff0c;中国通信标准化协会算网融合产业及标准推进委员会&#xff08;CCSATC621&#xff09;联合中国信息通信研究院&#xff0c;于2024年7月10日共同召开“2024年算网融合产业发展大会”。本次大会发布了多项算网融合领域最新研…

SpringBoot 日志:从基础到高级的全面指南

&#x1f4da; SpringBoot 日志&#xff1a;从基础到高级的全面指南 &#x1f50d; &#x1f4da; SpringBoot 日志&#xff1a;从基础到高级的全面指南 &#x1f50d;摘要引言正文内容一、日志概述 &#x1f4dc;二、日志使用 &#x1f4dd;2.1 打印日志 &#x1f4e3;2.2 日志…

主文件表遗失:数据恢复策略与实战指南

深入解析&#xff1a;无法恢复主文件表的困境 在数字化时代&#xff0c;数据不仅是信息的载体&#xff0c;更是企业运营和个人生活的核心。然而&#xff0c;当遭遇“无法恢复主文件表”的困境时&#xff0c;整个数据系统仿佛被按下了暂停键&#xff0c;让人措手不及。主文件表…

数据集成是什么意思?方法有哪些?数据集成三种方法介绍

1 数据集成是什么 数据集成(Data Intergration)&#xff0c;也称为数据整合&#xff0c;是通过将分布式环境中的异构数据集成起来&#xff0c;为用户提供统一透明的数据访问方式。该定义中的集成是指从整体层面上维护数据的一致性&#xff0c;并提高对数据的利用和共享&#x…