代码随想录算法训练营第十七天|654.最大二叉树 617.合并二叉树 700.二叉搜索树中的搜索 98.验证二叉搜索树

654.最大二叉树

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

  • 二叉树的根是数组中的最大元素。
  • 左子树是通过数组中最大值左边部分构造出的最大二叉树。
  • 右子树是通过数组中最大值右边部分构造出的最大二叉树。

通过给定的数组构建最大二叉树,并且输出这个树的根节点。

示例 :

654.最大二叉树

提示:

给定的数组的大小在 [1, 1000] 之间。

思路:

递归三部曲:

  1. 参数和返回值:数组可以在全局定义,不需要作为参数。参数是记录需要进行寻找最大值的数组区间的首尾两端下标即可。返回值应该是作为根节点的节点值。
  2. 终止条件:当首尾下标,left>right肯定是不正确的区间,说明区间不存在,直接返回None。如果left==right,说明区间只有一个数,直接将其作为节点返回。
  3. 递归逻辑:若是一个正常的区间,首先在这个区间中找到最大值下标,记录下来等着最后返回。由于当前处于区间[left,right],所以该节点middle会将区间分为两个部分,即left到middle,middle到right,在这两个区间分别调用递归作为该节点的两个孩子值,然后返回该节点。

代码实现如下:

# 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 constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:

        self.arr = nums

        return self.getmaxtree(0, len(nums)-1)

   

    def getmaxtree(self, left:int, right:int) -> Optional[TreeNode]:    # 左闭右闭

        if left > right:

            return None

        if left == right:

            node = TreeNode(self.arr[left])

            return node

        maxval = self.arr[left]

        middle = left

        for i in range(left+1, right+1):

            if self.arr[i] > maxval:

                maxval = self.arr[i]            # 这里记得要更新maxval值,debug才发现

                middle = i

        root = TreeNode(self.arr[middle])

        root.left = self.getmaxtree(left, middle-1)             # 注意是left,不是0

        root.right = self.getmaxtree(middle+1, right)           # 注意是right, 不是len(self.arr)-1

        return root

       

规范代码如下:

# 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 = rightclass Solution:

    def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:

        if len(nums) == 1:

            return TreeNode(nums[0])

        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:

            new_list = nums[:maxValueIndex]

            node.left = self.constructMaximumBinaryTree(new_list)

        # 最大值所在的下标右区间 构造右子树

        if maxValueIndex < len(nums) - 1:

            new_list = nums[maxValueIndex+1:]

            node.right = self.constructMaximumBinaryTree(new_list)

        return node

使用下标

class Solution:

    def traversal(self, nums: List[int], left: int, right: int) -> TreeNode:

        if left >= right:

            return 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))

使用切片:

class Solution:

    def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:

        if not nums:

            return 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.合并二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

617.合并二叉树

注意: 合并必须从两个树的根节点开始。

思路:

递归三部曲:

  1. 参数和返回值:两棵树的节点作为传入参数。返回一颗合并树,节点作为返回值。
  2. 终止条件:两个传入节点都为空。
  3. 递归逻辑:比较两个传入节点,如果一个为空,该节点值为另一个的值;否则为两个节点值之和,同时合并后的节点的孩子,是继续对两棵树对应位置的节点的递归调用得到的节点。需要考虑传入节点是否存在一个空值的情况,为了取得对应位置的节点,即本过程对应位置的左右孩子,若判断到其中一个节点为空值,为其赋值一个默认节点(属性为默认空值)。!!后来发现这部分考虑其实多余了,直接返回另外一个节点即可,因为返回另外一个节点,则其孩子也会被保留,而为空的节点本来就不存在孩子,对应位置的节点必然是非空节点的孩子。 但以下我自己的实现由于不是直接返回非空节点,所以还是需要有这一部分的操作考虑。

代码实现如下:

# 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 mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:

        return self.getnewnode(root1, root2)

       

    def getnewnode(self, node1: Optional[TreeNode], node2: Optional[TreeNode]) -> Optional[TreeNode]:

        if not node1 and not node2:

            return None

        node = TreeNode()

        if node1:

            node.val += node1.val

        else:

            node1 = TreeNode()

        if node2:

            node.val += node2.val

        else:

            node2 = TreeNode()

        node.left = self.getnewnode(node1.left, node2.left)

        node.right = self.getnewnode(node1.right, node2.right)

        return node

规范代码:

# 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 mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:

        # 递归终止条件:

        #  但凡有一个节点为空, 就立刻返回另外一个. 如果另外一个也为None就直接返回None.

        if not root1:

            return root2

        if not root2:

            return root1

        # 上面的递归终止条件保证了代码执行到这里root1, root2都非空.

        root1.val += root2.val # 中

        root1.left = self.mergeTrees(root1.left, root2.left) #左

        root1.right = self.mergeTrees(root1.right, root2.right) # 右

        

        return root1 # ⚠️ 注意: 本题我们重复使用了题目给出的节点而不是创建新节点. 节省时间, 空间.

迭代:

class Solution:

    def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:

        if not root1:

            return root2

        if not root2:

            return root1

        queue = deque()

        queue.append(root1)

        queue.append(root2)

        while queue:

            node1 = queue.popleft()

            node2 = queue.popleft()

            # 更新queue

            # 只有两个节点都有左节点时, 再往queue里面放.

            if node1.left and node2.left:

                queue.append(node1.left)

                queue.append(node2.left)

            # 只有两个节点都有右节点时, 再往queue里面放.

            if node1.right and node2.right:

                queue.append(node1.right)

                queue.append(node2.right)

            # 更新当前节点. 同时改变当前节点的左右孩子.

            node1.val += node2.val

            if not node1.left and node2.left:

                node1.left = node2.left

            if not node1.right and node2.right:

                node1.right = node2.right

        return root1


700.二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

例如,

700.二叉搜索树中的搜索

在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。

思路:

递归三部曲:

  1. 参数和返回值:参数为传入节点和搜索值。返回值为所找节点。
  2. 终止条件:找到了搜索值,返回该节点。遇到叶子节点且未找到搜索值,返回None。
  3. 递归逻辑:对当前节点的孩子继续调用递归,先对左孩子调用,如果存在返回值,则返回该值,否则继续对右孩子调用,如果存在则返回,不存在返回None。(忘记该题是二叉搜索树,应该判断节点大小和搜索值大小,如果搜索值小则搜索左孩子,否则搜索右孩子。自己实现的做法是任意二叉树)

代码实现如下:

# 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 searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:

        return self.findnode(root, val)

    def findnode(self, node: Optional[TreeNode], val: int) -> Optional[TreeNode]:

        if not node:

            return None

        if node.val == val:

            return node

        if (des := self.findnode(node.left, val)) or (des := self.findnode(node.right, val)):

            return des

        else:

            return None

规范代码:

class Solution:

    def searchBST(self, root: TreeNode, val: int) -> TreeNode:

        # 为什么要有返回值:

        #   因为搜索到目标节点就要立即return,

        #   这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。

        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)

迭代:

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

        return None

栈-遍历

class Solution:

    def searchBST(self, root: TreeNode, val: int) -> TreeNode:

        stack = [root]

        while stack:

            node = stack.pop()

            # 根据TreeNode的定义

            # node携带有三类信息 node.left/node.right/node.val

            # 找到val直接返回node 即是找到了该节点为根的子树

            # 此处node.left/node.right/val的前后顺序可打乱

            if node.val == val:

                return node

            if node.right:

                stack.append(node.right)

            if node.left:

                stack.append(node.left)

        return None


98.验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

98.验证二叉搜索树

思路:

一开始没自己实现,思路不多,但能想到可能会有的坑:在判断的时候不能只判断左右孩子是否小(大)于节点值,还需要注意无论是左右孩子,都要满足该节点和该节点父亲的关系保持一致。(例如:左孩子的右孩子不能大于本节点)

根据文字解析提供的思路:一颗二叉搜索树的中序遍历应该是一个有序数组,所以对该二叉树进行中序遍历得到一个数组,如果是有序的则验证成功,否则验证失败。

递归三部曲:

  1. 参数和返回值: 参数是传入节点。返回值是None
  2. 终止条件:节点为空。
  3. 递归逻辑:先对左孩子进行递归,然后对本节点进行操作(加入数组),然后对右孩子进行递归。

代码实现如下:

# 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 isValidBST(self, root: Optional[TreeNode]) -> bool:

        if not root:

            return True

        self.arr = []

        self.visit(root)

        maxvalue = self.arr[0]

        for i in range(1, len(self.arr)):

            if self.arr[i] <= maxvalue:

                return False

            maxvalue = self.arr[i]

        return True

    def visit(self, node: Optional[TreeNode]) -> None:

        if not node:

            return

        self.visit(node.left)

        self.arr.append(node.val)

        self.visit(node.right)

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

class Solution:

    def __init__(self):

        self.maxVal = float('-inf')  # 因为后台测试数据中有int最小值

    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

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

# 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 = rightclass 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

迭代法

# 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 = rightclass Solution:

    def isValidBST(self, root):

        stack = []

        cur = root

        pre = None  # 记录前一个节点

        while cur is not None or len(stack) > 0:

            if cur is not None:

                stack.append(cur)

                cur = cur.left  # 左

            else:

                cur = stack.pop()  # 中

                if pre is not None and cur.val <= pre.val:

                    return False

                pre = cur  # 保存前一个访问的结点

                cur = cur.right  # 右

        return True

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

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

相关文章

江科大笔记——新建工程

STM32的开发方式 目前STM32的开发方式主要有基于寄存器的方式、基于标准库的方式&#xff08;库函数的方式&#xff09;、基于HAL库的方式&#xff1a; 基于库函数的方式是使用ST官方提供的封装好的函数&#xff0c;通过调用这些函数来间接地配置寄存器。基于HAL库的方式可以…

【Linux】初始进程

目录 基本概念 PCB task_struct task_struct内容分类 组织进程 查看进程 查看正在运行的进程信息 获取pid和ppid 创建子进程 基本概念 一个已经加载到内存中的程序&#xff0c;叫做进程&#xff0c;正在运行的程序&#xff0c;叫做进程&#xff0c;进程是担当分配系统…

解决QT开发由于中文导致的编译错误以及输出内容乱码问题

在进行QT程序开发时&#xff0c;大家可能或者一定会遇到的问题就是中文乱码问题&#xff0c;这个乱码问题可能是在你看代码的显示上&#xff0c;也可能在程序的输出上&#xff0c;甚至还有可能导致你的代码直接编译失败&#xff0c;都有可能和中文编码有关&#xff0c;还有一些…

【Day20240924】联邦学习中的方法 改进

文章目录 前言一、FedAvg二、FedProx三、MOON四、FedDyn五、FedAsync六、PORT七、ASO-Fed八、FedBuff九、FedSA 前言 几种异步的方法&#xff1a; FedAsync PORT ASO-Fed FedBuff FedSA 几种同步的方法&#xff1a; FedAvg FedProx MOON FedDyn 一、FedAvg FedAvg基本步骤&a…

[SAP ABAP] 锁对象

在SAP中使用锁对象&#xff0c;用于避免在数据库中插入或更改数据时出现不一致的情况 1.创建锁对象 数据准备 学校表(ZDBT_SCH_437) 使用事务码SE11创建锁对象 点击"锁对象"单选按钮&#xff0c;输入以E开头的锁定对象的名称&#xff0c;然后点击创建按钮 锁对象名…

QT基础 制作简单登录界面

作业&#xff1a; 1、创建一个新项目&#xff0c;将默认提供的程序都注释上意义 01zy.pro代码 QT core gui # QT表示要引入的类库 core&#xff1a;核心库例如IO操作在该库中 gui&#xff1a;图形化界面库 # 如果要使用其他类库中的相关函数&#xff0c;则需要加对…

如何使用ssm实现基于web的学生就业管理系统的设计与实现+vue

TOC ssm726基于web的学生就业管理系统的设计与实现vue 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上…

努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂ROM固件-安卓刷机固件网

努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂ROM固件-安卓刷机固件网 统版本&#xff1a;官方软件作者&#xff1a;热心网友rom大小&#xff1a;911MB发布日期&#xff1a;2018-12-23 努比亚z17努比亚NX563j原厂固件卡刷包下载_刷机ROM固件包下载-原厂RO…

虚幻引擎游戏保存/加载存档功能

函数名功能Does Save Game Exist检查存档是否存在Load Game from Slot加载存档Save Game to Slot保存存档Delete Game in Slot删除存档 Slot Name 是插槽名字 存档都是通过插槽名字来 读取/加载/检查/删除的 先创建一个SaveGame类 , 这个类里可以存放要保存的数据 , 比如 玩家…

CSS 浏览器兼容问题探讨

目录 非 VIP 用户可前往公众号回复“css”进行免费阅读 浏览器介绍 css 选择器兼容介绍 ie6 微型盒子兼容解决方法 ie6双倍margin div中放入一个img元素导致div高度多出几像素 非 VIP 用户可前往公众号回复“css”进行免费阅读 浏览器介绍 在国内,常见的网页浏览…

华为认证HCIA篇--网络通信基础

大家好呀&#xff01;我是reload。今天来带大家学习一下华为认证ia篇的网络通信基础部分&#xff0c;偏重一些基础的认识和概念性的东西。如果对网络通信熟悉的小伙伴可以选择跳过&#xff0c;如果是新手或小白的话建议还是看一看&#xff0c;先有个印象&#xff0c;好为后续的…

机器学习:opencv--特征检测

目录 前言 一、 Harris 角点检测 1.基本思想 2.代码实现 二、 SIFT&#xff08;尺度不变特征变换&#xff09; 1.代码实现 前言 特征检测是计算机视觉中的一个重要任务&#xff0c;旨在从图像中提取具有辨识度的关键点或区域。这些特征可以用于后续的图像分析、匹配和识别…

25维谛技术面试最常见问题面试经验分享总结(包含一二三面题目+答案)

开头附上工作招聘面试必备问题噢~~包括综合面试题、无领导小组面试题资源文件免费&#xff01;全文干货。 【免费】25维谛技术面试最常见问题面试经验分享总结&#xff08;包含一二三面题目答案&#xff09;资源-CSDN文库https://download.csdn.net/download/m0_72216164/8979…

设计模式之策略设计模式

一、状态设计模式概念 策略模式&#xff08;Strategy&#xff09; 是一种行为设计模式&#xff0c; 它能让你定义一系列算法&#xff0c; 并将每种算法分别放入独立的类中&#xff0c; 以使算法的对象能够相互替换。 适用场景 当你想使用对象中各种不同的算法变体&#xff0c; …

【HTTP 和 HTTPS详解】3

HTTP 状态代码 HTTP 状态代码是服务器发送给客户端的三位数字&#xff0c;用于指示客户端请求的结果。它们分为五类&#xff1a;信息性&#xff08;100-199&#xff09;、成功&#xff08;200-299&#xff09;、重定向&#xff08;300-399&#xff09;、客户端错误&#xff08…

算法宝典——二分查找算法

1.认识二分查找 二分查找的时间复杂度:O(logN) 二分查找属于算法中耳熟能详的一类&#xff0c;通常的我们会说只有数组有序才可以使用二分查找&#xff0c;不过这种说法并不完全正确&#xff0c;只要数据具有"二段性"就可以使用二分查找&#xff0c;即我们可以找出一…

贪心的思想

803.区间合并 给定 n 个区间 [li,ri]&#xff0c;要求合并所有有交集的区间。 注意如果在端点处相交&#xff0c;也算有交集。 输出合并完成后的区间个数。 例如&#xff1a;[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。 输入格式 第一行包含整数 n。 接下来 n 行&#x…

Unity中的功能解释(数学位置相关和事件)

向量计算 Vector3.Slerp&#xff08;起点坐标&#xff0c;终点坐标&#xff0c;t&#xff09;&#xff0c;可是从起点坐标以一个圆形轨迹到终点坐标&#xff0c;有那么多条轨迹&#xff0c;那怎么办 Vector3.Slerp 进行的是沿球面插值&#xff0c;因此并不是沿着严格的“圆形…

【CSS】盒子模型

width 宽度 、height 高度 、padding 内边距 、margin 外边距 ( 外边距合并、子元素外边距塌陷问题 )border 边框border-radius 圆角box-shadow 阴影overflow 溢出float 浮动 ( 父元素塌陷问题 ) 盒子模型&#xff08;Box Model &#xff09;是指在网页设计中&#xff0c;用于描…