二叉搜索树题目:递增顺序搜索树

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法三
    • 思路和算法
    • 代码
    • 复杂度分析
  • 后记

题目

标题和出处

标题:递增顺序搜索树

出处:897. 递增顺序搜索树

难度

3 级

题目描述

要求

给定二叉搜索树的根结点 root \texttt{root} root,请你按中序遍历将其重新排列为递增顺序搜索树,使树中最左边的结点成为树的根结点,并且每个结点没有左子结点,只有右子结点。

示例

示例 1:

示例 1

输入: root = [5,3,6,2,4,null,8,1,null,null,null,7,9] \texttt{root = [5,3,6,2,4,null,8,1,null,null,null,7,9]} root = [5,3,6,2,4,null,8,1,null,null,null,7,9]
输出: [1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9] \texttt{[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]} [1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]

示例 2:

示例 2

输入: root = [5,1,7] \texttt{root = [5,1,7]} root = [5,1,7]
输出: [1,null,5,null,7] \texttt{[1,null,5,null,7]} [1,null,5,null,7]

数据范围

  • 树中结点数目在范围 [1, 100] \texttt{[1, 100]} [1, 100]
  • 0 ≤ Node.val ≤ 1000 \texttt{0} \le \texttt{Node.val} \le \texttt{1000} 0Node.val1000

解法一

思路和算法

这道题要求按照二叉搜索树中序遍历的顺序将二叉搜索树重新排列为递增顺序搜索树。最直观的解法是对二叉搜索树中序遍历并记录中序遍历的结点顺序,然后更改结点之间的指针指向。对于每个结点,将其左子结点指针设为 null \text{null} null,将其右子结点指针设为中序遍历顺序的后一个结点,其中中序遍历顺序的最后一个结点的右子结点指针也设为 null \text{null} null

新的根结点为中序遍历序列的首个结点,返回新的根结点。

代码

下面的代码为递归实现二叉搜索树中序遍历的做法。

class Solution {List<TreeNode> traversal = new ArrayList<TreeNode>();public TreeNode increasingBST(TreeNode root) {inorder(root);int size = traversal.size();for (int i = 0; i < size; i++) {TreeNode node = traversal.get(i);node.left = null;node.right = i == size - 1 ? null : traversal.get(i + 1);}return traversal.get(0);}public void inorder(TreeNode node) {if (node == null) {return;}inorder(node.left);traversal.add(node);inorder(node.right);}
}

下面的代码为迭代实现二叉搜索树中序遍历的做法。

class Solution {public TreeNode increasingBST(TreeNode root) {List<TreeNode> traversal = new ArrayList<TreeNode>();Deque<TreeNode> stack = new ArrayDeque<TreeNode>();TreeNode node = root;while (!stack.isEmpty() || node != null) {while (node != null) {stack.push(node);node = node.left;}node = stack.pop();traversal.add(node);node = node.right;}int size = traversal.size();for (int i = 0; i < size; i++) {node = traversal.get(i);node.left = null;node.right = i == size - 1 ? null : traversal.get(i + 1);}return traversal.get(0);}
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉搜索树的结点数。中序遍历需要访问每个结点一次,展开为链表也需要访问每个结点一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉搜索树的结点数。中序遍历的递归实现和迭代实现都需要栈空间,栈空间取决于二叉搜索树的高度,最坏情况下二叉搜索树的高度是 O ( n ) O(n) O(n),存储中序遍历序列需要 O ( n ) O(n) O(n) 的空间。

解法二

思路和算法

解法一需要访问每个结点两次,首先得到中序遍历序列,然后重新排列。其实,中序遍历和重新排列可以同步完成,只需要访问每个结点一次。

在中序遍历的迭代实现中,维护上一个访问的结点 prev \textit{prev} prev 和当前访问的结点 curr \textit{curr} curr,初始时 prev = null \textit{prev} = \text{null} prev=null。对于每个结点,当 prev ≠ null \textit{prev} \ne \text{null} prev=null 时,将 prev \textit{prev} prev 的左子结点指针设为 null \text{null} null,将 prev \textit{prev} prev 的右子结点指针设为 curr \textit{curr} curr。访问当前结点 curr \textit{curr} curr 之后,将 prev \textit{prev} prev 的值设为 curr \textit{curr} curr,继续访问下一个结点,直到遍历结束。

遍历结束时, prev \textit{prev} prev 为最后一个访问的结点,将其左子结点指针和右子结点指针都设为 null \text{null} null。新的根结点为首个访问的结点,返回新的根结点。

代码

class Solution {public TreeNode increasingBST(TreeNode root) {Deque<TreeNode> stack = new ArrayDeque<TreeNode>();TreeNode newRoot = null;TreeNode prev = null, curr = root;while (!stack.isEmpty() || curr != null) {while (curr != null) {stack.push(curr);curr = curr.left;}curr = stack.pop();if (newRoot == null) {newRoot = curr;}if (prev != null) {prev.left = null;prev.right = curr;}prev = curr;curr = curr.right;}prev.left = null;prev.right = null;return newRoot;}
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉搜索树的结点数。每个结点都被访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉搜索树的结点数。空间复杂度主要是栈空间,栈内元素个数不超过 n n n

解法三

思路和算法

解法一和解法二都需要使用栈空间。还有一种使用常数空间的解法,不使用栈或者其他数据结构存储结点,只改变结点之间的指针关系。

为了方便处理,创建哑结点,将二叉搜索树的根结点作为哑结点的右子结点,则哑结点没有左子结点,只有右子结点。从哑结点开始遍历,则遍历到的结点(即当前结点)一定没有左子结点。如果当前结点有右子结点,则执行如下操作。

  • 如果右子结点没有左子结点,则将当前结点移动到右子结点,继续对剩下的结点重新排列。

  • 如果右子结点有左子结点,则找到右子结点的前驱结点,将右子结点作为前驱结点的右子结点,将右子结点的左子结点作为当前结点的右子结点。

重复上述操作,直到当前结点的右子结点为空,此时二叉搜索树中的结点排列完毕,哑结点的右子结点为新的根结点,返回新的根结点。

考虑示例 1 的二叉搜索树,其中序遍历顺序是 [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] [1,2,3,4,5,6,7,8,9] [1,2,3,4,5,6,7,8,9]。此处省略哑结点。

图 1

当前结点为哑结点,右子结点为结点 5 5 5,结点 5 5 5 的左子结点为结点 3 3 3,因此找到结点 5 5 5 的前驱结点 4 4 4,将以结点 5 5 5 为根结点的子树移动到结点 4 4 4 的右子树的位置,将以结点 3 3 3 为根结点的子树移动到哑结点的右子树的位置。

图 2

当前结点为哑结点,右子结点为结点 3 3 3,结点 3 3 3 的左子结点为结点 2 2 2,因此找到结点 3 3 3 的前驱结点 2 2 2,将以结点 3 3 3 为根结点的子树移动到结点 2 2 2 的右子树的位置,将以结点 2 2 2 为根结点的子树移动到哑结点的右子树的位置。

图 3

当前结点为哑结点,右子结点为结点 2 2 2,结点 2 2 2 的左子结点为结点 1 1 1,因此找到结点 2 2 2 的前驱结点 1 1 1,将以结点 2 2 2 为根结点的子树移动到结点 1 1 1 的右子树的位置,将以结点 1 1 1 为根结点的子树移动到哑结点的右子树的位置。

图 4

结点 1 1 1 至结点 6 6 6 的左子结点都为空,因此将当前结点移动到结点 6 6 6

当前结点为结点 6 6 6,右子结点为结点 8 8 8,结点 8 8 8 的左子结点为结点 7 7 7,因此找到结点 8 8 8 的前驱结点 7 7 7,将以结点 8 8 8 为根结点的子树移动到结点 7 7 7 的右子树的位置,将以结点 7 7 7 为根结点的子树移动到结点 6 6 6 的右子树的位置。

图 5

结点 7 7 7 至结点 9 9 9 的左子结点都为空,因此将当前结点移动到结点 9 9 9

此时当前结点的右子结点为空,二叉搜索树中的结点排列完毕。

代码

class Solution {public TreeNode increasingBST(TreeNode root) {TreeNode dummyRoot = new TreeNode(0, null, root);TreeNode node = dummyRoot;while (node.right != null) {TreeNode right = node.right;if (right.left != null) {TreeNode predecessor = right.left;while (predecessor.right != null) {predecessor = predecessor.right;}predecessor.right = right;TreeNode next = right.left;right.left = null;node.right = next;} else {node = node.right;}}return dummyRoot.right;}
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉搜索树的结点数。遍历和展开过程中,每个结点都被访问一次,寻找前驱结点的过程中,每个结点最多被访问一次,因此每个结点最多被访问两次。

  • 空间复杂度: O ( 1 ) O(1) O(1)

后记

读者也许已经发现,解法三和莫里斯遍历非常相似。和莫里斯遍历相比,这道题不需要将前驱结点的右指针指向当前结点,而是将当前结点的右子树移动到前驱结点的右子树的位置,因此解法三可以看成莫里斯遍历的简化版。

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

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

相关文章

P1498 南蛮图腾题解

题目 给定一个正整数n&#xff0c;参考输出样例&#xff0c;输出图形。 输入输出格式 输入格式 每个数据输入一个正整数n&#xff0c;表示图腾的大小&#xff08;此大小非彼大小&#xff09; 输出格式 这个大小的图腾 输入输出样例 输入样例 2 输出样例 /\/__\/\ /\…

HTTP缓存技术

大家好我是苏麟 , 今天说说HTTP缓存技术 . 资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) HTTP缓存技术 HTTP 缓存有哪些实现方式? 对于一些具有重复性的 HTTP 请求&#xff0c;比如每次请求得到的数据都一样的&#xff0c;我们可以把这对「请求-响…

【mysql】数据约束

一、数据约束&#xff1a; 什么是约束&#xff1f; 为了确保表中的数据的完整性(准确性、正确性)&#xff0c;为表添加一些限制。是数据库中表设计的一个最基本规则。使用约束可以使数据更加准确&#xff0c;从而减少冗余数据&#xff08;脏数据&#xff09;。 数据库完整性约…

84 CTF夺旗-PHP弱类型异或取反序列化RCE

目录 案例1&#xff1a;PHP-相关总结知识点-后期复现案例2&#xff1a;PHP-弱类型对比绕过测试-常考点案例3&#xff1a;PHP-正则preg_match绕过-常考点案例4&#xff1a;PHP-命令执行RCE变异绕过-常考点案例5&#xff1a;PHP-反序列化考题分析构造复现-常考点涉及资源&#xf…

【HTML】过年不能放烟花,那就放电子烟花

闲谈 大家回家过年可能都多多少少放过些&#x1f9e8;&#xff0c;但是有些在城市上过年的小伙伴可能就没有机会放鞭炮了。不过没关系&#xff0c;我们懂技术&#xff0c;我们用技术自娱自乐&#xff0c;放电子烟花&#xff0c;总不可能被警长叔叔敲门问候吧。 开干 首先&…

Redis篇----第一篇

系列文章目录 文章目录 系列文章目录前言一、什么是 Redis?二、Redis 与其他 key-value 存储有什么不同?三、Redis 的数据类型?四、使用 Redis 有哪些好处?五、Redis 相比 Memcached 有哪些优势?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住…

Spring 用法学习总结(四)之 JdbcTemplate 连接数据库

&#x1f409;目录 9 JdbcTemplate 9 JdbcTemplate Spring 框架对 JDBC 进行了封装&#xff0c;使用 JdbcTemplate 方便实现对数据库操作 相关包&#xff1a; 百度网盘链接https://pan.baidu.com/s/1Gw1l6VKc-p4gdqDyD626cg?pwd6666 创建properties配置文件 &#x1f4a5;注意…

分布式文件系统 SpringBoot+FastDFS+Vue.js【二】

分布式文件系统 SpringBootFastDFSVue.js【二】 六、实现上传功能并展示数据6.1.创建数据库6.2.创建spring boot项目fastDFS-java6.3.引入依赖6.3.fastdfs-client配置文件6.4.跨域配置GlobalCrosConfig.java6.5.创建模型--实体类6.5.1.FastDfsFile.java6.5.2.FastDfsFileType.j…

vscode 和 keil协同使用开发stm32程序,超详细教程

vscode 和 keil协同使用开发stm32程序 文章目录 vscode 和 keil协同使用开发stm32程序1. 安装vscode拓展安装chinese插件 2 .安装Mingw3.配置环境变量4. 打开Keil项目 VSCODE 是一款广受好评的代码编辑器&#xff0c; KEIL 是常用的嵌入式开发工具但编程界面简陋。 将两个工具…

npm使用国内淘宝镜像(最新地址)

目录 前言 一、命令配置 二、使用cnpm安装 三、常见包地址 四、总结 往期回顾 前言 我们前端程序员在使用国外的镜像源速度很慢并且容易下载失败&#xff0c;有时候需要尝试多次才有可能下载成功&#xff0c;很麻烦&#xff0c;但是可以切换为国内镜像源&#xff0c;下…

BigDecimal的常用API

BigDecimal用于解决浮点型运算时结果出现失真的问题。 这里0.20.1等于0.3就出现了失真 import java.math.BigDecimal; import java.math.RoundingMode;public class Test {public static void main(String[] args) {//BigDeciaml的使用&#xff1a;解决小数运算失真的问题doub…

微信网页版能够使用(会顶掉微信app的登陆)

一、文件结构 新建目录chrome新建icons&#xff0c;其中图片你自己找吧新建文件manifest.json新建文件wx-rules.json 二、文件内容 对应的png你们自己改下 1、manifest.json {"manifest_version": 3,"name": "wechat-need-web","author…

C语言第二十五弹---字符函数和字符串函数(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 目录 1、字符分类函数 2、字符转换函数 3、strlen的使用和模拟实现 4、strcpy 的模拟实现 5、strcat 的模拟实现 6、strcmp 的模拟实现 7、strncpy 函数的使用 总结…

Excel练习:日历

Excel练习&#xff1a;日历 ‍ 题目&#xff1a;制作日历 ‍ ​​ 用rows和columns函数计算日期单元格偏移量 一个公式填充所有日期单元格 ​​ ‍

CSP-201909-1-小明种苹果

CSP-201909-1-小明种苹果 #include <iostream> using namespace std; int main() {long long sumApple 0, maxNum 0, maxAppleNum 0, n, m;cin >> n >> m;for (long long i 0; i < n; i){long long appleNum, delta 0;cin >> appleNum;for (l…

【JavaEE】spring boot快速上手

SpringBoot快速上手 文章目录 SpringBoot快速上手Maven会出现的一个官方bug创建完项目之后常用的的三个功能依赖管理Maven仓库中央仓库本地仓库国内源配置私服 springboot项目创建什么是springspring boot项目的创建Hello Worldweb服务器 SpringMVC什么是SpringWebMVC什么是MVC…

111. 二叉树的最小深度

给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明&#xff1a;叶子节点是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;2示例 2&#xff1a; 输入…

Three.js学习9:Three.js 响应式设计

-----------------------------华丽的分割线--------------------- 相关代码均已上传到 gitee 中&#xff1a;myThree: 学习 Three.js &#xff0c;努力加油~&#xff01; Gitee 静态演示地址&#xff1a;Three JS 演示页面 -----------------------------华丽的分割线------…

相机图像质量研究(17)常见问题总结:CMOS期间对成像的影响--靶面尺寸

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

【AIGC】Stable Diffusion的常见错误

Stable Diffusion 在使用过程中可能会遇到各种各样的错误。以下是一些常见的错误以及可能的解决方案&#xff1a; 模型加载错误&#xff1a;可能出现模型文件损坏或缺失的情况。解决方案包括重新下载模型文件&#xff0c;确保文件完整并放置在正确的位置。 依赖项错误&#x…