二叉排序树的后序遍历序列必然是递增的_剑指offer 33——二叉搜索树的后序遍历序列...

本题主要在于考察对二叉搜索树和后序遍历的理解。

原题

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

参考以下这颗二叉搜索树:

     5    /    2   6  /  1   3

示例 1:

输入: [1,6,3,2,5]输出: false

示例 2:

输入: [1,3,2,6,5]输出: true

提示:

  • 数组长度 <= 1000

原题url:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/

解题

基本概念

首先介绍一些基本概念,方便后续做题。

  1. 后序遍历:[ 左子树 | 右子树 | 根节点 ] ,即遍历顺序为 “左、右、根” 。
  2. 二叉搜索树: 左子树中所有节点的值 < 根节点的值;右子树中所有节点的值 > 根节点的值;其左、右子树也分别为二叉搜索树。

递归分治

既然本题只提供了后序遍历,那么我们就要在此基础之上下功夫了。

根据上面的提供的说明,后序遍历是,先左右子树再根节点,那么根是容易判断的,肯定在整个序列的最后。

而二叉搜索树节点值满足,左子树 < 根 < 右子树,因此我们就可以以根为基础,从后向前遍历,。

一开始的节点肯定都大于根,因为都在右子树上,一旦出现小于根的节点,说明就进入了左子树,那么之后所有的节点都应该小于根。然后再分别遍历左右子树,直至到叶子节点为止(即无左右子树的节点)。

按照上面的方法,就需要我们将后序遍历分成左右子树,不断递归遍历检查。

接下来看看代码:

class Solution {    public boolean verifyPostorder(int[] postorder) {        return checkTree(postorder, 0, postorder.length - 1);    }    private boolean checkTree(int[] postorder, int start, int end) {        // 如果start >= end,说明已经寻找结束        if (start >= end) {            return true;        }        // 找到根        int root = postorder[end];        // 左子树开始的下标        int leftStart = start;        // 左子树结束的下标        int leftEnd = leftStart;                // 找到第一个大于根节点的值        while (leftEnd < end && postorder[leftEnd] < root) {            leftEnd++;        }        leftEnd--;        // 右子树开始的下标        int rightStart = leftEnd + 1;        // 右子树结束的下标        int rightEnd = end - 1;        // 检查右子树是否都大于根节点        for (int i = rightStart; i < end; i++) {            if (postorder[i] > root) {                continue;            }            return false;        }        // 继续检查左右子树        return checkTree(postorder, leftStart, leftEnd) &&                checkTree(postorder, rightStart, rightEnd);    }}

提交OK。

分析一下复杂度:

  • 时间复杂度 O(N^2) : 每次调用 checkTree 方法减去一个根节点,因此递归占用 O(N) ;最差情况下(即当树退化为链表),每轮递归都需遍历树所有节点,占用 O(N ^ 2) 。
  • 空间复杂度 O(N) : 最差情况下(即当树退化为链表),递归深度将达到 N 。

递增栈

既然上面分析出时间复杂度为 O(N^2) ,那么是否可以找到一种更高效的方法,只遍历一次序列,就可以解决问题呢?因为这样可以在时间复杂度上进行很大的优化。

这就需要再进一步结合搜索二叉树和后序遍历的特性了。(这个方法我是在网上看到的,感觉属于一种比较偏门的优化,一般很难想出这种方法)

在我们从后向前遍历序列时,大致是经历了根、右子树、左子树,而左子树 < 根 < 右子树,那么一开始应该是单调递增的,我们可以将这些节点依次入栈。

当不满足单调递增调试时,一般是碰到了右子树中某一个左子树节点,或者真正的左子树,这时候可以将栈顶元素出栈,直到碰到比当前节点小的元素,那么将最后的栈顶元素设为根节点。

此时继续遍历,应该保证所有节点都小于根节点,因为此时已经进入左子树序列了。否则说明该序列不满足搜索二叉树的后序遍历。

重复以上步骤,如果遍历结束,说明满足搜索二叉树的后序遍历。

这么说可能比较难懂,直接上代码:

class Solution {    public boolean verifyPostorder(int[] postorder) {        // 单调递增栈        Stack stack = new Stack<>();        int root = Integer.MAX_VALUE;        // 倒序遍历        for (int i = postorder.length - 1; i >= 0; i--) {            if (postorder[i] > root) {                return false;            }            // 如果当前栈不为空,且当前遍历的节点小于栈顶节点            while (!stack.isEmpty() &&                 postorder[i] < stack.peek()) {                // 栈顶节点压出,且更新根节点                root = stack.pop();            }            // 当前节点入栈            stack.push(postorder[i]);        }        return true;    }}

提交OK。

分析一下复杂度:

  • 时间复杂度 O(N) : 遍历 postorder 所有节点,各节点均入栈 / 出栈一次,使用 O(N) 时间。
  • 空间复杂度 O(N) : 最差情况下(即当树退化为链表),单调递增栈 stack 存储所有节点。

神奇的是,力扣给出的执行结果显示:递归分治方法消耗的时间更短。这点大家也可以研究研究是为什么。

总结

以上就是这道题目我的解答过程了,不知道大家是否理解了。本题主要在于考察对二叉搜索树和后序遍历的理解,递归分治是容易想出来的方法,但是后面那种单调递增栈确实很难想到,可以作为一种特殊思路进行理解。

有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。

https://death00.github.io/

公众号:健程之道

784259e44e6ec461ed5301986c0a045b.png
e27105c0804abc60e260bded30303b95.png

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

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

相关文章

LeetCode 1880. 检查某单词是否等于两单词之和

文章目录1. 题目2. 解题1. 题目 字母的 字母值 取决于字母在字母表中的位置&#xff0c;从 0 开始 计数。即&#xff0c;a -> 0、b -> 1、c -> 2&#xff0c;以此类推。 对某个由小写字母组成的字符串 s 而言&#xff0c;其 数值 就等于将 s 中每个字母的 字母值 按…

python求矩阵的秩_Python 实现线性代数计算器

计算逆序数&#xff0c;运用O(N^2)的算法计算# 逆序数计算def reversePairs(nums):"""计算数组的逆序数&#xff0c;输入数组&#xff0c;返回逆序数"""res, n 0, len(nums)for i in range(1,n):for j in range(0,i):if nums[j]>nums[i]:res …

查询去除空值_SQL数据处理(五):SQL多表查询

一. 表的加法加法(union)。合并两张数据表course和course1/去除重复行重合【select 课程号,课程名称 from course union select 课程号,课程名称 from course1;】 输出结果两个表合并的数据若要保留重复的行数据(union all)【select 课程号,课程名称 from course union all sel…

WPS Office文档未保存怎么恢复

有时候用WPS Office时&#xff0c;文档还没保存&#xff0c;因为电脑卡死或者关机&#xff0c;再次打开时编辑的内容都不见了&#xff0c;这个时候可以利用WPS自带的备份功能来恢复文档&#xff0c;表格、幻灯片、文档都是可以的。 首先单击WPS左上角的按钮&#xff0c;选择&qu…

LeetCode 664. 奇怪的打印机(区间DP)

文章目录1. 题目2. 解题1. 题目 有台奇怪的打印机有以下两个特殊要求&#xff1a; 打印机每次只能打印由 同一个字符 组成的序列。每次可以在任意起始和结束位置打印新字符&#xff0c;并且会覆盖掉原来已有的字符。 给你一个字符串 s &#xff0c;你的任务是计算这个打印机…

程序闪退怎么运行_苹果应用程序崩溃闪退怎么办?如何解决苹果设备的软故障?...

你们在使用苹果设备的时候是不是经常遇到程序卡死&#xff0c;或者苹果设备假死的情况&#xff1f;如果您遇到程序崩溃闪退或报错的时候&#xff0c;请不要慌张&#xff0c;按照以下方法解决问题。苹果设备它是应用程序问题还是设备故障&#xff1f;首先&#xff0c;您必须弄清…

lxrun不是内部或外部命令_在Win10 安装 WSL的Linux子系统,解决cmd中执行lxrun时提示“不是内部或外部命令”...

Win10 安装Linux子系统原理就是安装一个&#xff08;Linux系统的&#xff09;应用程序。使用应用程序一般的使用Linux子系统&#xff0c;非常方便。第一步&#xff1a;启用开发者模式启用开发者模式&#xff0c;其实很简单&#xff0c;本人参考的是该文章&#xff1a;手把手教你…

LeetCode 1886. 判断矩阵经轮转后是否一致

文章目录1. 题目2. 解题1. 题目 给你两个大小为 n x n 的二进制矩阵 mat 和 target 。 现 以 90 度顺时针轮转 矩阵 mat 中的元素 若干次 &#xff0c;如果能够使 mat 与 target 一致&#xff0c;返回 true &#xff1b; 否则&#xff0c;返回 false 。 示例 1&#xff1a; …

ggplot2中显示坐标轴_qplot()——ggplot2的快速绘图

先前写过几篇用ggplot2进行基本绘图的文章&#xff0c;但对于初学者&#xff0c;或只需绘制简单图形时&#xff0c;这些命令显得繁琐&#xff0c;这里介绍ggplot2中的快速绘图函数qplot(). 此函数相对能较快速便捷地绘制图形。往期文章&#xff1a;张光耀&#xff1a;ggplot2数…

python清洗文本数据_02.数据预处理之清洗文本信息

准备30万条新闻数据编号新闻类别新闻数量(条)1财经370982教育419633科技655344时政630865体育655346娱乐65534yield生成器斐波那契数列介绍和数学描述斐波那契数列算法实现斐波那契数列算法生成器实现算法时间性能对比分析# codingutf-8"""Description&#xff…

学习思考 耐得寂寞 拥得繁华

要么读书&#xff0c;要么旅行&#xff0c;身体和灵魂&#xff0c;必须有一个在路上。 生活不可能像想象得那么好&#xff0c;但也不会像想象得那么糟。我觉得人的脆弱和坚强都超乎自己的想象。有时&#xff0c;我可能脆弱得一句话就泪流满面&#xff0c;有时&#xff0c;也发现…

python比较时间的最大值_时间戳的最大值

我在用3.6.1 |Continuum Analytics, Inc.| (default, May 11 2017, 13:09:58) \n[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]在Windows10机器上运行的Ubuntu16.04VM中。在我把你的ctime调用分解到它的各个部分&#xff0c;去调查&#xff0c;但是我没有达到同样的最大值。在>&g…

LeetCode 879. 盈利计划(动态规划)

文章目录1. 题目2. 解题1. 题目 集团里有 n 名员工&#xff0c;他们可以完成各种各样的工作创造利润。 第 i 种工作会产生 profit[i] 的利润&#xff0c;它要求 group[i] 名成员共同参与。如果成员参与了其中一项工作&#xff0c;就不能参与另一项工作。 工作的任何至少产生…

【Unity】对象池技术

【Unity】对象池技术 1.简介 对象池&#xff08;Object Pooling&#xff09;技术是一种用于优化游戏性能和内存管理的技术。 对象池可以用于各种需要频繁实例化和销毁游戏对象的场景&#xff0c;例如子弹、敌人、玩家等。其主要目标是减少频繁的内存分配和垃圾回收的次数&…

正则邮箱_自己写一个通用的邮箱正则表达式

今天把正则又复习了一遍&#xff0c;为了加深记忆&#xff0c;自己写一个邮箱的正则表达式咱们先来看几个合法的邮箱地址hd33322nat123.commaksim.kim.82d-link.uavova_lazarev1969jaagers.comb4-service.szhaopinmail.com通过观察上面几个例子&#xff0c;不难看出所有邮箱地址…

[css][移动设备]禁止横竖屏时内容自动调整

参考&#xff1a;http://www.kankanews.com/ICkengine/archives/106643.shtml iOS下当竖屏转向横屏的时候&#xff0c;发现内容字体会自动变大&#xff0c;通过各种方法设置字体大小都失败了。 需要在body中设置如下&#xff1a; text-size-adjust: none;-ms-text-size-adjust…

python gettext_python gettext使用

python中使用gettext进行语言国际化的方法1.编辑源代码, 保存为gettextdemo.pyimport gettextcatalogs gettext.find("example", localedir"locale", allTrue)print catalogs:,catalogst gettext.translation(example, "locale", fallbackTrue…

python 数字、字符串与 time时间/日期 的转换

获取当前时间戳 >>> import time >>> num time.time() # 当前时间戳, 7位浮点 >>> type(num) <class float> >>> num 1623302086.1892786数字 转 时间 >>> t time.localtime(num) # 数字 转 时间 >>> type(t…

python基础版课件_python基础课件精编版.ppt

6.10 问题解答 (1)能不能像执行.exe文件那样执行.py文件呢&#xff1f; 在Windows上是不行的&#xff0c;但是&#xff0c;在Mac和Linux上是可以的&#xff0c;方法是在.py文件的第一行加上一个特殊的注释。 (2)在实际项目中&#xff0c;条件语句用的多还是循环语句用的多&…

Convert Windows 32bit dirver to Windows 64bit

Pre-condition: 1.source code(vc6.0WDK based) Development environment: 2.VS2013 3.WDK 8/8.1 Steps: 1.Create a new project. File->New->Visual C->Windows Driver 2.Copy .c and .h source code to project 3.Modify project property xxx属性页->配置属性-…