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 rootn = 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 Noneroot = TreeNode(preorder[left])if left == right: return rootl, r = left, rightwhile(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 rootn = len(preorder)if n==0: return nullreturn 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 = 0def dfs(lowerBound: int, upperBound: int):nonlocal index  # 将index声明为非局部变量if index == n: return Nonecur = preorder[index]if cur < lowerBound or cur > upperBound: return Noneindex += 1root = TreeNode(cur)root.left = dfs(lowerBound, cur)root.right = dfs(cur, upperBound)return rootif n==0: return nullreturn 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 nullroot = 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 = currentNodeelse : node.left = currentNodestack.append(currentNode)return root

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

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

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

相关文章

【UE5】监控摄像头效果(下)

目录 效果 步骤 一、多摄像机视角切换 二、摄像头自动旋转巡视 三、摄像头跟踪拍摄 效果 步骤 一、多摄像机视角切换 1. 打开玩家控制器“MyPlayerController”&#xff0c;添加一个变量&#xff0c;命名为“BP_SecurityCameraArray”&#xff0c;类型为“BP_SecurityCa…

蛋仔派对巅峰对决惊奇舞台必胜打法

Hello!大家好呀&#xff01;蛋仔派对我也开始玩啦&#xff01;本期我们发一个蛋仔派对巅峰对决惊奇舞台的必胜打法吧&#xff01;&#xff08;题外话&#xff1a;我的蛋仔名叫做 酷影kuying 大家能加我的好友吗&#xff1f;我是新手有老手能带我上分嘛&#xff1f;…

第二十四章 STL-函数对象

一、函数对象 1、函数对象概念 概念&#xff1a; 重载函数调用操作符的类&#xff0c;其对象常称为函数对象 函数对象使用重载的()时&#xff0c;行为类似函数调用&#xff0c;也叫仿函数 本质&#xff1a; 函数对象(仿函数)是一个类&#xff0c;不是一个函数 2、函数对…

[方法论]allocation 空间内容分配

区分度 typeanalysisrecognitionconclusion type - 阅读 - 理解- 背诵- 听课 看 听 思考- reproduce/ 默写/ 应用- 背- 想- 写analysis 理解 和 背 是不占用现实空间的&#xff0c;可以在脑内不断消化&#xff0c;可以飞配给没有空间的时间块。 阅读 和 写是占用现实空间的…

企业如何选择合适的信息化管理系统?

一、什么是信息化管理系统 信息化这个词在近年已经被说烂了&#xff0c;在信息化快速发展的时代&#xff0c;越来越多的企业开始意识到信息化管理系统的重要性。信息化管理系统是指一种能够帮助企业或组织有效管理信息资源&#xff0c;提高信息的可靠性、安全性和有效性的软件…

博世汽车产业转型,裁1500人 | 百能云芯

博世&#xff08;Bosch&#xff09;&#xff0c;作为全球领先的汽车零部件制造商&#xff0c;近日宣布了一项战略性的组织调整计划&#xff0c;以更好地适应不断演变的汽车行业需求和技术革新。根据《路透社》的报道&#xff0c;博世计划在2025年底之前&#xff0c;在其位于德国…

【OD2023C卷真题】20天拿下华为OD笔试之【排序】2023C-身高提供排序【欧弟算法】全网注释最详细分类最全的华为OD真题题解

文章目录 题目描述与示例题目描述输入描述输出描述示例一输入输出 示例二输入输出 解题思路代码时空复杂度 进阶华为OD算法/大厂面试高频题算法练习冲刺训练 题目描述与示例 题目描述 某学校举行运动会,学生们按编号(1、2、3.....n) 进行标识, 现需要按照身高由低到高排列&a…

Redis基础系列-主从复制

Redis基础系列-主从复制 文章目录 Redis基础系列-主从复制1. 什么是 Redis 主从复制&#xff1f;2. 主从复制有什么好处&#xff1f;3. 如何配置 Redis 主从复制&#xff1f;4. 主从复制的验证4.1 如何查看主从搭建成功4.2 主从常见疑问4.3 主从常见命令 5. 主从复制的原理和工…

掌握1688官方API接口:开启智能商务合作新篇章

当涉及到与1688官方合作的API接口时&#xff0c;以下是一些建议和指导&#xff0c;以帮助您开始编写相关的代码。 了解API接口文档&#xff1a; 在编写与1688官方合作的API接口之前&#xff0c;首先需要了解1688官方提供的API接口文档。您可以在1688开放平台上找到相关的文档…

12.11 作业

1&#xff0c; 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码…

王道数据结构课后代码题p150 第13——17 (c语言代码实现)

目录 13.p 和 q 分别为指向该二叉树中任意两个结点的指针&#xff0c;试编写算法 ANCESTOR(ROOT,P,q,r)&#xff0c;找到P和q的最近公共祖先结点 r 14.假设二叉树采用二叉链表存储结构&#xff0c;设计一个算法&#xff0c;求非空二叉树 b的宽度(即具有结点数最多的那一层的结点…

Draw.io绘图操作

使用步骤 以下是使用 draw.io&#xff08;现在的 diagrams.net&#xff09;的一些基本操作步骤&#xff1a; 访问网站&#xff1a; 打开浏览器&#xff0c;访问 https://app.diagrams.net/。 创建新文档&#xff1a; 在 diagrams.net 主页&#xff0c;点击 “New Diagram” 或…

2023最新vue安装(npm,yarn,国内镜像,vue安装,vue导包)全套教程2023年12月最新

第一步(安装npm) 官网地址&#xff1a;https://nodejs.org/en/download windows安装yarn 详细教程_windows yarn-CSDN博客 第二步&#xff08;yarn下载&#xff09; windows 下需要下载msi文件 &#xff0c;下载地址&#xff1a;https://yarnpkg.com/latest.msi npm install -g…

力扣198. 打家劫舍

动态规划 思路&#xff1a; 寻找状态转移方程&#xff1a; 假设有 n 个房间&#xff1b; 如果偷第 n 个房间&#xff0c;那么第 n - 1 个房间不偷&#xff0c;之前的 n - 2 个房间偷窃到了 M(n - 2)&#xff0c;总共可以偷窃到 M(n - 2) N(n)&#xff1b;如果不偷第 n 个房间…

第11节: Vue3 动态参数

在UniApp中使用Vue3框架使用动态参数&#xff1a; <template> <view> <text>{{ dynamicText }}</text> <button click"changeText">点击改变文本</button> </view> </template> <script> export de…

SD-WAN解决企业国际互联组网需求

随着云计算、移动应用和企业全球化的浪潮&#xff0c;实时应用在不同地点之间的传输需求不断增加&#xff0c;涵盖异地办公、视频会议、远程桌面、支付交易系统以及远程医疗等。这些应用的顺畅传输对于企业至关重要&#xff0c;而SD-WAN&#xff08;软件定义广域网&#xff09;…

Spring MVC详解、静态资源访问、拦截器

1. Spring MVC概述 1.1 Spring MVC是什么 SpringMVC是Spring的一个模块&#xff0c;是一个基于MVC设计模式的web框架。 1.2 Spring MVC执行流程。 1.3 组件分析 前端控制器&#xff08;默认配置&#xff09;Dispatcher Servlet 作用&#xff1a;只负责分发请求。可以很好的对…

这样的软件测试面试题,谁面试遇到谁淘汰!!!

88 11.6 自动化测试用例的来源 手工编写测试用例 把原来手工的测试用例&#xff0c;当成自动化测试用例 11.7 自动化测试的优点与缺点 优点: 1、对程序的回归测试更方便 2、可以运行更多更繁琐的测试 3、提高测试效率和准确性&#xff0c;节约时间成本 4、可以执行一些手工测试…

【源码解析】从ReentrantLock角度聊聊AQS原理

AQS结构 //头节点 当前持有锁的线程private transient volatile Node head;/*** Tail of the wait queue, lazily initialized. Modified only via* method enq to add new wait node.*///每个进来的线程都插入到最后private transient volatile Node tail;/*** The synchroni…

MLIR笔记(6)

5. 方言与操作 5.1. 方言的概念 在MLIR里&#xff0c;通过Dialect类来抽象方言。具体的每种方言都需要从这个基类派生一个类型&#xff0c;并实现重载自己所需的虚函数。 MLIR文档里这样描述方言&#xff08; MLIR Language Reference - MLIR&#xff09;&#xff1a; 方言…