LeetCode-1008. 前序遍历构造二叉搜索树【栈 树 二叉搜索树 数组 二叉树 单调栈】

LeetCode-1008. 前序遍历构造二叉搜索树【栈 树 二叉搜索树 数组 二叉树 单调栈】

  • 题目描述:
  • 解题思路一:题目大致意思就是给定一个二叉树的前序遍历,求对应的二叉搜索树。一种比较特殊的点是「二叉搜索树」的中序遍历的结果是【有序序列】,故而我们可以对「前序遍历」的结果 【排序】 得到「中序遍历」的结果。从而依据这棵树的前序和中序遍历结果构建该「二叉搜索树」。
  • 解题思路二:二分查找左右子树的分界线递归构建左右子树。可以找到的规律是前序遍历结果第一个是根节点,而后面的元素可以分为两个连续的数组,一个数组所有元素严格小于根节点,另一个数组所有元素严格大于根节点。困难在于快速找到这两个数组的分界线,这里用的是二分法。
  • 解题思路三:根据数值上下界递归构建左右子树,我们使用递归的方法,在扫描先序遍历的同时构造出二叉树。我们在递归时维护一个 (lower, upper) 二元组,表示当前位置可以插入的节点的值的上下界。如果此时先序遍历位置的值处于上下界中,就将这个值作为新的节点插入到当前位置,并递归地处理当前位置的左右孩子的两个位置。否则回溯到当前位置的父节点。
  • 解题思路四:单调栈,思路是维护一个栈顶小栈顶大的单调栈,遍历一遍前序遍历结果。如果当前元素大于栈顶元素,则循环出栈找到其父节点node。如果父节点的元素值小于子节点的元素值,则子节点为右孩子,否则为左孩子。代码逻辑其实很简单。

题目描述:

给定一个整数数组,它表示BST(即 二叉搜索树 )的 先序遍历 ,构造树并返回其根。

保证 对于给定的测试用例,总是有可能找到具有给定需求的二叉搜索树。

二叉搜索树 是一棵二叉树,其中每个节点, Node.left 的任何后代的值 严格小于 Node.val , Node.right 的任何后代的值 严格大于 Node.val。

二叉树的 前序遍历 首先显示节点的值,然后遍历Node.left,最后遍历Node.right。

示例 1:
请添加图片描述
输入:preorder = [8,5,1,7,10,12]
输出:[8,5,10,1,7,null,12]

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

提示:
1 <= preorder.length <= 100
1 <= preorder[i] <= 10^8
preorder 中的值 互不相同

解题思路一:题目大致意思就是给定一个二叉树的前序遍历,求对应的二叉搜索树。一种比较特殊的点是「二叉搜索树」的中序遍历的结果是【有序序列】,故而我们可以对「前序遍历」的结果 【排序】 得到「中序遍历」的结果。从而依据这棵树的前序和中序遍历结果构建该「二叉搜索树」。

对应的题目是105. 从前序与中序遍历序列构造二叉树,感兴趣的可以看看。

# 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 bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        inorder = sorted(preorder)
        def myBST(preorder_left: int, preorder_right: int, inorder_left: int, inorder_right: int):
            if preorder_left > preorder_right:
                return None
            
            # 前序遍历中的第一个节点就是根节点
            preorder_root = preorder_left
            # 在中序遍历中定位根节点
            inorder_root = index[preorder[preorder_root]]

            # 先把根节点建立出来
            root = TreeNode(preorder[preorder_root])
             # 得到左子树中的节点数目
            size_left_subtree = inorder_root - inorder_left
            # 递归地构造左子树,并连接到根节点
            # 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
            root.left = myBST(preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1)
            # 递归地构造右子树,并连接到根节点
            # 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
            root.right = myBST(preorder_left + 1 + size_left_subtree, preorder_right, inorder_root + 1, inorder_right)
            return root

        n = len(preorder)
        index = {element: i for i, element in enumerate(inorder)}
        return myBST(0, n-1, 0, n-1)

构造哈希表的目的是为了,O(1)时间找到中序遍历结果中的根节点。
时间复杂度:O(nlogn)排序的结果,构造二叉搜索树的时间复杂度为 O(n)
空间复杂度:O(n)

解题思路二:二分查找左右子树的分界线递归构建左右子树。可以找到的规律是前序遍历结果第一个是根节点,而后面的元素可以分为两个连续的数组,一个数组所有元素严格小于根节点,另一个数组所有元素严格大于根节点。困难在于快速找到这两个数组的分界线,这里用的是二分法。

# 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 bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        def dfs(preorder: List[int], left: int, right: int):
            if left > right: return None
            root = TreeNode(preorder[left])
            if left == right: return root

            l, r = left, right
            while(l < r):
                mid = int(l + (r - l + 1) / 2)
                if preorder[mid] < preorder[left]: l = mid # 转到区间[mid, r]
                else : r = mid -1 # 转到区间[l, mid -1]
            # 其实最后l=r是最终的分界线
            root.left = dfs(preorder, left + 1, l)
            root.right = dfs(preorder, l + 1, right)
            return root

        n = len(preorder)
        if n==0: return null
        return dfs(preorder, 0, n-1)

时间复杂度:O(nlogn),在找左右子树分界线的时候时间复杂度为O(logn);
空间复杂度:O(n)

解题思路三:根据数值上下界递归构建左右子树,我们使用递归的方法,在扫描先序遍历的同时构造出二叉树。我们在递归时维护一个 (lower, upper) 二元组,表示当前位置可以插入的节点的值的上下界。如果此时先序遍历位置的值处于上下界中,就将这个值作为新的节点插入到当前位置,并递归地处理当前位置的左右孩子的两个位置。否则回溯到当前位置的父节点。

请添加图片描述

# 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 bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        n = len(preorder)
        index = 0
        def dfs(lowerBound: int, upperBound: int):
            nonlocal index  # 将index声明为非局部变量
            if index == n: return None
            cur = preorder[index]
            if cur < lowerBound or cur > upperBound: return None

            index += 1
            root = TreeNode(cur)
            root.left = dfs(lowerBound, cur)
            root.right = dfs(cur, upperBound)
            return root
        if n==0: return null
        return dfs(float('-inf'), float('inf'))

时间复杂度:O(n)
空间复杂度:O(n)

解题思路四:单调栈,思路是维护一个栈顶小栈顶大的单调栈,遍历一遍前序遍历结果。如果当前元素大于栈顶元素,则循环出栈找到其父节点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 bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        n = len(preorder)
        if n==0: return null
        root = TreeNode(preorder[0])
        stack= [root]
        for i in range(1,n,1):
            node = stack[-1]
            currentNode = TreeNode(preorder[i])
            while stack and stack[-1].val < currentNode.val: node = stack.pop()
            if node.val < currentNode.val: node.right = currentNode
            else : node.left = currentNode
            stack.append(currentNode)
        return root

时间复杂度:O(n)仅扫描前序遍历一次。
空间复杂度:O(n)用来存储栈和二叉树。

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

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

相关文章

智慧水利-城市水循环可视化助力城市水资源可持续发展

2021年《中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要》明确指出&#xff1a;构建智慧水利体系&#xff0c;以流域为单元提升水情测报和智能调度能力。水利部按照“需求牵引、应用至上、数字赋能、提升能力”总要求&#xff0c;编制了《“十四五”智…

深入理解强化学习——马尔可夫决策过程:预测与控制

分类目录&#xff1a;《深入理解强化学习》总目录 预测&#xff08;Prediction&#xff09;和控制&#xff08;Control&#xff09;是马尔可夫决策过程里面的核心问题。预测&#xff08;评估一个给定的策略&#xff09;的输入是马尔可夫决策过程 < S , A , R , P , γ > …

中职网络安全应急响应—Server2228

应急响应 任务环境说明: 服务器场景:Server2228(开放链接) 用户名:root,密码:p@ssw0rd123 1. 找出被黑客修改的系统别名,并将倒数第二个别名作为Flag值提交; 通过用户名和密码登录系统 在 Linux 中,利用 “alias” 命令去查看当前系统中定义的所有别名 flag:ss …

LeetCode:1631. 最小体力消耗路径(SPFA Java)

目录 1631. 最小体力消耗路径 题目描述&#xff1a; 实现代码与解析&#xff1a; BFSDP 原理思路&#xff1a; 1631. 最小体力消耗路径 题目描述&#xff1a; 你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights &#xff0c;其中 heights[row][col] 表…

python 爬虫 m3u8 视频文件 加密解密 整合mp4

文章目录 一、完整代码二、视频分析1. 认识m3u8文件2. 获取密钥&#xff0c;构建解密器3. 下载ts文件4. 合并ts文件为mp4 三、总结 一、完整代码 完整代码如下&#xff1a; import requests import re import os from tqdm import tqdm from Crypto.Cipher import AES# 创建临…

通过Jmeter压测存储过程

一、存储过程准备&#xff1a; 1、建立一个空表&#xff1a; 1 CREATE TABLE test_data ( id NUMBER, name VARCHAR2(50), age NUMBER ); 2、建立一个存储过程&#xff1a; CREATE OR REPLACE PROCEDURE insert_test_data(n IN NUMBER) ASBEGIN--EXECUTE IMMEDIATE trunca…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion 黑白老照片上色修复

在这个时代,我们习惯于拥有高清、色彩丰富的照片,然而,那些古老的黑白色老照片由于年代的久远,往往会出现模糊、破损等现象。 那么今天要给大家介绍的是,用 Stable Diffusion 来修复老照片。 前段时间 ControlNet 的除了上线了“IP-Adapter”模型以外还增加另一个…

JVM虚拟机系统性学习-对象存活判断算法、对象引用类型和垃圾清除算法

垃圾回收 在 JVM 中需要对没有被引用的对象&#xff0c;也就是垃圾对象进行垃圾回收 对象存活判断算法 判断对象存活有两种方式&#xff1a;引用计数法、可达性分析算法 引用计数法 引用计数法通过记录每个对象被引用的次数&#xff0c;例如对象 A 被引用 1 次&#xff0c…

决策报表布局方式(新建一个绝对布局,双击,在拖其它图表,报表块装进去。就不会变形)

FineReport11.0 1.绝对布局&#xff1a; 只是适合自己调试电脑显示&#xff0c;适用于一个展示区域需要用多个组件叠加组成时使用 2.自适应布局&#xff1a;双向自适应 &#xff1a; https://help.fanruan.com/finereport/doc-view-4276.html 组件会自动调整显示宽度以适应不…

实战React18和TS+Vite,跟进实战阅读类App的心得分享

随着 React 18 的发布&#xff0c;以及 TypeScript 和 Vite 在前端开发领域的普及&#xff0c;使用 React 18 结合 TypeScript 和 Vite 开发实战阅读类 App 的经验已经成为了前端开发者们的热门话题。在本文中&#xff0c;我将分享我的心得体会&#xff0c;并给出一些示例代码&…

12.11_黑马数据结构与算法笔记Java

目录 070 栈 链表实现 概念理清&#xff1a;什么时候是指针的指向&#xff0c;什么时候是元素本身&#xff1f; 071 栈 数组实现 072 栈 e01 有效的括号 072 栈 e02 后缀表达式求值 072 栈 e03 中缀表达式转后缀1 072 栈 e03 中缀表达式转后缀2 072 栈 e03 中缀表达式转…

Linux之进程(三)(环境变量)

目录 一、基本概念 二、环境变量 1、PATH 2、HOME 3、SHELL 三、环境变量参数 四、argc和argv 一、基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。如&#xff1a;临时文件夹位置和系统文件夹位置等。环境变量通常…

通用的AGI 安全风险

传统安全风险 平台基础设施安全风险 模型与数据层安全风险 应用层安全风险 平台基础设施安全风险 &#xff08;1&#xff09;物理攻击&#xff1a;机房管控不到位 &#xff08;2&#xff09;网络攻击 &#xff08;3&#xff09;计算环境&#xff1a;自身安全漏洞&#xf…

Java - Mybatis的缓存机制、集成SpringBoot后缓存相关问题

mybaits提供一级缓存&#xff0c;和二级缓存 一级缓存&#xff08;默认开启&#xff09; 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象&#xff0c;在对象中有一个(内存区域)数据结构&#xff08;HashMap&#xff09;用于存储缓存数据。不同的sqlSe…

【Java SE】带你识别什么叫做异常!!!

&#x1f339;&#x1f339;&#x1f339;个人主页&#x1f339;&#x1f339;&#x1f339; 【&#x1f339;&#x1f339;&#x1f339;Java SE 专栏&#x1f339;&#x1f339;&#x1f339;】 &#x1f339;&#x1f339;&#x1f339;上一篇文章&#xff1a;【Java SE】带…

U5 符号表管理

文章目录 一、语义分析1、任务 二、符号表1、概述2、操作3、基本结构4、组织方式 三、非分程序的符号表1、概念2、标识符的作用域及基本处理办法3、符号表的组织方式 四、分程序的符号表&#xff1a;处理作用域嵌套1、概念2、处理方法 五、栈式符号表六、基于符号表的存储组织与…

Appilied energy论文复现:计及光伏电站快速无功响应特性的分布式电源优化配置方法程序代码!

本程序参考Applied energy论文《Optimal siting and sizing of distributed generation in distribution systems with PV solar farm utilized as STATCOM (PV-STATCOM)》&#xff0c;文中主要对光伏电站、微燃机等分布式电源进行优化配置&#xff0c;程序较为简单和基础&…

SD生成的图像不清晰,如何解决

文生图 选择高清修复&#xff1a; 几点注意 重绘幅度&#xff1a;这里不用太高&#xff0c;他会根据你生成的低分辨率图像&#xff0c;生成高分辨率的图像&#xff0c;可以选择0.3~05之间&#xff0c;给AI跟多想象力空间可以选择0.5 ~ 0.7。太低边缘模糊&#xff0c;太高了可能…

《数字图像处理-OpenCV/Python》连载(56)图像的灰度直方图

《数字图像处理-OpenCV/Python》连载&#xff08;56&#xff09;非线性灰度变换 本书京东 优惠购书链接 https://item.jd.com/14098452.html 本书CSDN 独家连载专栏 https://blog.csdn.net/youcans/category_12418787.html 第 8 章 图像的直方图处理 图像的直方图是反映像素值…

约瑟夫问题

目录 方法一&#xff1a;数组模拟 方法二&#xff1a;链表模拟 方法三&#xff1a;数学递归 约瑟夫问题&#xff1a; 编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数&#xff0c;报到 m 的人离开。下一个人继续从 1 开始报数。 n-1 轮结束以后&#xff0c;只剩下…