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

news2024/12/26 0:04:45

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)用来存储栈和二叉树。

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

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

相关文章

参数占位符#{}和${}

#是预处理而$是直接替换 Mybatis在处理#{}时&#xff0c;会将SQL中的#{}替换成占位符&#xff1f;&#xff0c;再使用preparedStatement的set方法来赋值。而Mybatis在处理 时&#xff0c;是将 {}时&#xff0c;是将 时&#xff0c;是将{}直接替换成变量的值 我们分别使用#{}和…

热电厂发电机组常见故障及预测性维护方法

热电厂的发电机组是关键的能源生产设备&#xff0c;在电力供应中扮演着关键角色。但经过长期运行和高负荷工作&#xff0c;一旦发生故障&#xff0c;可能导致停机、设备损坏甚至引发严重事故。因此&#xff0c;实施有效的预测性维护方法对于确保发电机组的稳定运行至关重要。本…

vue3封装接口

在src下面创建一个文件夹任意名称 我拿这个名字举例子了apiService 相当于创建一个新的文件 // 封装接口 // apiService.js import axios from axios;// 接口前缀 const API_BASE_URL 前缀;接口后缀export const registerUser async (fileData) > {try {const response …

Node CLI 之 Yargs (2)

什么是 yargs&#xff1f; yargs 是一个用于处理命令行参数的 Node.js 库 安装 yargs npm install yargs简单例子 不定义任何选项&#xff0c;直接便可以使用 定义命令 const yargs require(yargs)yargs.command(hello, Prints hello world, (yargs) > {}, (argv) >…

栈(深入理解栈是什么)

这里写目录标题 栈概念栈的初始化栈的溢出函数的栈帧函数的返回 栈 概念 英文&#xff1a;stack&#xff0c;也叫做堆栈。 特点&#xff1a;先进后出。 栈的两个基本操作&#xff0c;也就是入栈和出栈。都是通过SP指针来维护。C语言中的函数的局部变量&#xff0c;传递的实参…

datav-实现轮播表,使用updateRows方法-无缝衔接加载数据

前言 最近在做大屏需求的时候&#xff0c;遇到一个轮播数据的需求&#xff0c;查看datav文档发现确实有这个组件 但这个组件只提供了一次加载轮播的例子&#xff0c;虽然提供了轮播加载数据updateRows方法 但是文档并没有触发事件&#xff0c;比如轮播完数据触发事件&#xf…

RocketMQ-RocketMQ高性能核心原理节点(流程图)

NamesrvServer启动流程图&#xff1a; namesrvServer启动简图&#xff1a; Broker服务启动过程流程图 Broker服务启动过程流程简图 整体RPC框架流程如下图 client: DefaultMQProducer

CTF刷题记录

刷题 我的md5脏了KFC疯狂星期四坤坤的csgo邀请simplePHPcurl 我的md5脏了 g0at无意间发现了被打乱的flag&#xff1a;I{i?8Sms??Cd_1?T51??F_1?} 但是好像缺了不少东西&#xff0c;flag的md5值已经通过py交易得到了&#xff1a;88875458bdd87af5dd2e3c750e534741 flag…

2024美赛备战1--数据处理(数据预处理,异常值处理,预测模型,插值拟合 *****必看****)

1.数据预处理 所谓数据预处理&#xff0c;就是指在正式做题之前对数据进行的一些处理。在有些情 况下&#xff0c;出题方提供的数据或者网上查找的数据并不能直接使用&#xff0c;比如缺少数据甚 至是异常数据&#xff0c;如果直接忽略缺失值&#xff0c;或者没发现异常数据&am…

idea java 创建 hellword 项目

1.概要 建立一个最简单的工程&#xff0c;开始编码。 2.试验过程 2.1 创建的过程很简单&#xff0c;除了第一个窗口选择“java 模块”&#xff0c;其他的都是下一步。 2.2 还有就是刚创建的工程什么都没有需要&#xff0c;自己创建一个启动的类和启动函数。 2.3 运行和运行…

随机生成验证码的jar包

这是已经开发好的验证码&#xff0c;咱们直接调用接口&#xff0c;拿过来直接用就可以了 链接&#xff1a;https://pan.baidu.com/s/1QMPhW5UzxmhIa7THFab5hw 提取码&#xff1a;6666 下面演示一下&#xff1a; 首先创建一个Code来先生成随机验证码&#xff0c;然后传…

一个音乐能够做成二维码吗?音乐的活码制作技巧

一个音乐能够做成二维码后展示吗&#xff1f;现在以二维码为载体来储存内容的方式越来越常见&#xff0c;比如图片、文件、视频、音频都可以做成二维码展示&#xff0c;人们也更习惯去扫码获取内容。音频作为日常工作生活中常用的一种内容&#xff0c;可以用音频二维码生成器来…

如何在Word中简洁地插入代码

如何在Word中简洁地插入代码 背景&#xff1a; ​ 最近在一写一些论文或者报告的时候&#xff0c;需要将源代码放在论文的最后&#xff0c;有一个很头疼的问题&#xff0c;如果直接把代码从编辑器复制到word中&#xff0c;就变成了下面这个样子&#xff1a; 这有点丑陋啊&…

2023-12学习笔记

1.NonNull要手动写无参构造器 这是一个我今天研究了很久的问题&#xff0c;开始不知道原因是在这里&#xff0c;还在那想是不是Data覆盖了无参构造&#xff0c;结果当然不是。先说下解决历程 1.问题起因 通过RequestBody接收前端报文的时候报错&#xff0c;大致是说我构造方…

【算法提升—力扣每日一刷】五日总结【12/06--12/10】

2023/12/06 力扣每日一刷&#xff1a;206. 反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2…

马云说的AI电商真的要来了?AR技术虚拟试穿公司ZERO10引入AI人工智能模型,未来试衣间就应这样!

ZERO10 是一家提供虚拟试穿体验的公司&#xff0c;他们基于自家的 AR 技术&#xff0c;提供高度逼真且顶尖的虚拟试穿体验。与现有的技术不同&#xff0c;他们的生成式人工智能试穿技术只需要 1-5 张用户照片&#xff0c;就可以实现虚拟试穿。这种方法为规模化应用提供了巨大机…

jquery手写广告轮播图,无限循环功能

说明 在很多情况下&#xff0c;我们都需要开发广告轮播图&#xff0c;当我们进行页面的功能开发时&#xff0c;采用轮播图来实现也行&#xff0c;但是很多情况下&#xff0c;我们只需要简单的控制轮播循环轮播展示即可&#xff0c;所以用jq开开发广告轮播波&#xff0c;自定义…

NR重写console.log 增加时间信息

如题&#xff0c;默认console.log输出的日志是13位的时间戳&#xff0c;然后不方便查看与对比代码运行点的耗时&#xff0c;我们可以简单地重写 console.log方法&#xff0c;增加自定义时间戳格式&#xff0c;如下是增加时间&#xff08;时&#xff0c;分&#xff0c;秒&#x…

dirmap 工具学习

dirmap 1.1 工具的下载路径1.2 工具的安装流程1.3 工具的详细使用参数说明测试实验 1.1 工具的下载路径 dirmap 是一个高级 web 目录扫描工具&#xff0c;基于 python3 开发、运行&#xff0c;功能将会强于 DirBuster、Dirsearch、cansina、御剑。 github 下载地址 1.2 工具…

日本服务器:确保其稳定性的几个要点

​  在租用日本服务器时&#xff0c;用户们大多一定会关注它的稳定性&#xff0c;其实这些顾及都是正常的。毕竟&#xff0c;网站要想正常运行&#xff0c;保障服务器稳定是关键。本文将讨论有关如何保障日本服务器稳定性的一些有用技巧&#xff0c;希望对您有所帮助。 1.注重…