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

原题

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 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/

公众号:健程之道

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

  1. 【LeetCode】剑指 Offer 33. 二叉搜索树的后序遍历序列

    [LeetCode]剑指 Offer 33. 二叉搜索树的后序遍历序列 文章目录 [LeetCode]剑指 Offer 33. 二叉搜索树的后序遍历序列 package offer;public cl ...

  2. 剑指 Offer 33. 二叉搜索树的后序遍历序列

    剑指 Offer 33. 二叉搜索树的后序遍历序列 原始题目链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian- ...

  3. 【超100%解法】剑指 Offer 33. 二叉搜索树的后序遍历序列

    立志用最少的代码做最高效的表达 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果.如果是则返回 true,否则返回 false.假设输入的数组的任意两个数字都互不相同. 示例 1: 输入: ...

  4. 【分治】剑指 Offer 33. 二叉搜索树的后序遍历序列

    题目描述 (中等)输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果.如果是则返回 true,否则返回 false.假设输入的数组的任意两个数字都互不相同. 参考以下这颗二叉搜索树: 5/ ...

  5. 剑指offer 33. 二叉搜索树的后序遍历

    声明:本系列博客是对何海涛<剑指offer>的关键点总结. 1.问题描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果,如果是返回true,否则返回false.假设输入的数 ...

  6. 2021-08-24剑指 Offer 33. 二叉搜索树的后序遍历序列

    将二叉树划分为左右子树,分别对左右子树进行验证是否为二叉树, 根节点的下标为i, 右子树的开端为第一个大于根节点的数字m, 则左边的子树为[0,m-1],右边的子树的下标为[m,i-1] (这个不对, ...

  7. 剑指offer之二叉搜索树的后序遍历序列

    剑指offer之二叉搜索树的后序遍历序列 欢迎关注作者博客 简书传送门 题目 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个 ...

  8. 一刷328-递归recur-剑指 Offer 33. 二叉搜索树的后序遍历序列(m)

    题目: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果.如果是则返回 true,否则返回 false. 假设输入的数组的任意两个数字都互不相同. ----------- 示例:参考以下这 ...

  9. 剑指笔记——33.二叉搜索树的后序遍历序列

    题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No. 假设输入的数组的任意两个数字都互不相同. 思路:在这个题中要注意是二叉搜索树,二叉搜索树满足左 ...

最新文章

  1. Winform程序怎么降低占用的内存?
  2. linux磁盘管理系列一:磁盘配额管理
  3. c语言复制的代码不能运行,刚学C语言,在Linux下写的代码能正常编译,复制到VC下就无法运行...
  4. 41. 缺失的第一个正数 golang
  5. 835. Trie字符串统计
  6. UNUSED参数,这个宏,很秀
  7. js 字符串、数值之间的转换
  8. django之创建第8-2个项目-数据库数据提取之过滤操作符相关
  9. matlab2c使用c++实现matlab函数系列教程-acos函数
  10. Django 一些少用却很实用的orm查询方法
  11. 三招解决!电脑密码忘记了怎么办?新手都能学会
  12. UWB技术的工作原理
  13. 为什么网站用手机移动4G网络打不开?
  14. Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.4.2.RELEASE:repackage (de
  15. 中石油大学22春季《大学英语(四)#》第一阶段在线作业
  16. linux下批量转换32bit wav为16bit
  17. 谷歌浏览器驱动官网下载
  18. 小丸子学Docker系列之——安装Docker及基本命令的使用
  19. IntelliJ IDEA 如何配置MySQL数据库
  20. TDM-GCC MINGW配置与优化算法测试函数使用

热门文章

  1. u-boot启动参数命令格式
  2. 从源码到可执行程序的步骤
  3. iSCSI存储设备的udev绑定 以及iscsi重启卡住解决方法
  4. (转)初次体验.net Ajax无刷新技术
  5. PWM调光方法在LED亮度调节中的应用
  6. C++11 新特性 —— 关键字noexcept
  7. A star算法优化二
  8. Linux进程O(1)调度算法,面试必考哦
  9. Android Input子系统-含实例源码
  10. 解读Android 4.0 Camera原生应用程序的设计思路