前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >剑指offer | 面试题26:二叉搜索树的后序遍历序列

剑指offer | 面试题26:二叉搜索树的后序遍历序列

作者头像
千羽
发布2021-12-29 13:23:18
3130
发布2021-12-29 13:23:18
举报
文章被收录于专栏:程序员千羽

死磕算法系列文章

  1. 干货 | 手撕十大经典排序算法
  2. 剑指offer | 认识面试
  3. 剑指offer | 面试题2:实现Singleton模式
  4. 剑指offer | 面试题3:二维数组的查找
  5. 剑指offer | 面试题4:替换空格
  6. 剑指offer | 面试题5:从尾到头打印链表
  7. 剑指offer | 面试题6:重建二叉树
  8. 剑指offer | 面试题7:用两个栈实现队列
  9. 剑指offer | 面试题8:旋转数组的最小数字
  10. 剑指offer | 面试题9:斐波那契数列
  11. 剑指offer | 面试题10:青蛙跳台阶问题
  12. 剑指offer | 面试题11:矩阵覆盖
  13. 剑指offer | 面试题12:二进制中1的个数
  14. 剑指offer | 面试题13:数值的整数次方
  15. 剑指offer | 面试题14:打印从1到最大的n位数
  16. 剑指offer | 面试题15:删除链表的节点
  17. 剑指offer | 面试题16:将数组中的奇数放在偶数前
  18. 剑指offer | 面试题17:链表中倒数第k个节点
  19. 剑指offer | 面试题18:反转链表
  20. 剑指offer | 面试题19:合并两个有序链表
  21. 剑指offer | 面试题20:判断二叉树A中是否包含子树B
  22. 剑指offer | 面试题21:二叉树的镜像
  23. 剑指offer | 面试题22:顺时针打印矩阵
  24. 剑指offer | 面试题23:包含min函数的栈
  25. 剑指offer | 面试题24:栈的压入、弹出序列
  26. 剑指offer | 面试题25:从上到下打印二叉树

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

GitHub : https://github.com/nateshao/leetcode/blob/main/algo-notes/src/main/java/com/nateshao/sword_offer/topic_26_verifyPostorder/Solution.java

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

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

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

代码语言:javascript
复制
    5
   / \
  2   6
 / \
1   3

示例 1:

代码语言:javascript
复制
输入: [1,6,3,2,5]
输出: false

示例 2:

代码语言:javascript
复制
输入: [1,3,2,6,5]
输出: true

解题思路:

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

方法一:递归

  • 根据二叉搜索树的定义,可以通过递归,判断所有子树的 正确性 (即其后序遍历是否满足二叉搜索树的定义) ,若所有子树都正确,则此序列为二叉搜索树的后序遍历。
递归解析:
  • 终止条件: 当 i ≥ j,说明此子树节点数量 ≤ 1 ,无需判别正确性,因此直接返回 true ;
  • 递推工作:
    • 左子树区间[i, m- 1]内的所有节点都应< postorder[j]。而第1. 划分左右子树步骤已经保证左子树区间的正确性,因此只需要判断右子树区间即可。
    • 右子树区间[m,j- 1]内的所有节点都应> postorder[j] 。实现方式为遍历,当遇到≤ postorder[j] 的节点则跳出;则可通过 p = j 判断是否为二叉搜索树。
    1. 划分左右子树:遍历后序遍历的[i,j] 区间元素,寻找第一个大于根节点的节点,索引记为m 。此时,可划分出左子树区间[i,m- 1]、 右子树区间[m,j- 1]、 根节点索引 j。
    2. 判断是否为二叉搜索树:
  • 返回值:所有子树都需正确才可判定正确,因此使用 与逻辑符 &&连接。
    1. p=j :判断此树否正确。
    2. recur(i,m- 1) :判断 此树的左子树 否正确。
    3. recur(m,j-1) :判断 此树的右子树 是否正确。

复杂度分析:

  • 时间复杂度 O(N^2) : 每次调用 recur(i,j) 减去一个根节点,因此递归占用 O(N) ;最差情况下(即当树退化为链表),每轮递归都需遍历树所有节点,占用 O(N) 。
  • 空间复杂度 O(N) : 最差情况下(即当树退化为链表),递归深度将达到 N 。
代码语言:javascript
复制
package com.nateshao.sword_offer.topic_26_verifyPostorder;

import java.util.Stack;

/**
 * @date Created by 邵桐杰 on 2021/11/30 14:22
 * @微信公众号 程序员千羽
 * @个人网站 www.nateshao.cn
 * @博客 https://nateshao.gitee.io
 * @GitHub https://github.com/nateshao
 * @Gitee https://gitee.com/nateshao
 * Description:
 */
public class Solution {
    public static void main(String[] args) {
        int[] postorder = {1, 3, 2, 6, 5};
        int[] postorder2 = {1, 6, 3, 2, 5};// false
        int[] postorder3 = {1, 6, 3, 2, 5};// false
        System.out.println("递归:(postorder) = " + verifyPostorder(postorder));
        System.out.println("递归:(postorder2) = " + verifyPostorder(postorder2));
    }
    
    public static boolean verifyPostorder(int[] postorder) {
        return recur(postorder, 0, postorder.length - 1);
    }

    public static boolean recur(int[] postorder, int i, int j) {
        if (i >= j) return true;
        int p = i;
        while (postorder[p] < postorder[j]) p++;
        int m = p;
        while (postorder[p] > postorder[j]) p++;
        return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
    }
    /**
     * 递归:(postorder) = true
     * 递归:(postorder2) = false
     */
}

方法二:栈

后序遍历 倒序: [ 根节点 | 右子树 | 左子树 ] 。类似 先序遍历的镜像 ,即先序遍历为 “根、左、右” 的顺序,而后序遍历的倒序为 “根、右、左” 顺序。

算法流程:

  1. 初始化: 单调栈stack,父节点值root = +∞(初始值为正无穷大, 可把树的根节点看为此无穷大节点的左孩子) ;
  2. 倒序遍历postorder: 记每个节点为ri;
    1. 判断: 若ri > root,说明此后序遍历序列不满足一 叉搜索树定义,直接返回false ;
    2. 更新父节点root: 当栈不为空 且ri < stack:peek()时,循环执行出栈,并将出栈节点赋给root
    3. 入栈: 将当前节点ri入栈;
  3. 若遍历完成,则说明后序遍历满足二叉 搜索树定义,返回true。
代码语言:javascript
复制
package com.nateshao.sword_offer.topic_26_verifyPostorder;

import java.util.Stack;

/**
 * @date Created by 邵桐杰 on 2021/11/30 14:22
 * @微信公众号 程序员千羽
 * @个人网站 www.nateshao.cn
 * @博客 https://nateshao.gitee.io
 * @GitHub https://github.com/nateshao
 * @Gitee https://gitee.com/nateshao
 * Description: 二叉搜索树的后序遍历序列
 */
public class Solution {
    public static void main(String[] args) {
        int[] postorder3 = {1, 6, 3, 2, 5};// false
        System.out.println("栈:(postorder3) = " + verifyPostorder2(postorder3));
    }

    /**
     * 方法二:栈
     *
     * @param postorder
     * @return
     */
    public static boolean verifyPostorder2(int[] postorder) {
        Stack<Integer> stack = new Stack<>();
        //初始化: 单调栈 stackstack ,父节点值 root = +∞ (初始值为正无穷大,可把树的根节点看为此无穷大节点的左孩子);
        int root = Integer.MAX_VALUE;
        // 倒序遍历 postorder:记每个节点为 i
        for (int i = postorder.length - 1; i >= 0; i--) {
            // 若 ri>root,说明此后序遍历序列不满足二叉搜索树定义,直接返回 false ;
            if (postorder[i] > root) return false;
            // 更新父节点root:当栈不为空且 stack.peek()>ri时,循环执行出栈,并将出栈节点赋给 root
            while (!stack.isEmpty() && stack.peek() > postorder[i])
                root = stack.pop();
            stack.add(postorder[i]); // 入栈: 将当前节点 ri
        }
        return true; //遍历完成,则说明后序遍历满足二叉搜索树定义,返回 true 。
    }
    /**
     * 递归:(postorder) = true
     * 递归:(postorder2) = false
     * 栈:(postorder3) = false
     */
}

参考链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/solution/mian-shi-ti-33-er-cha-sou-suo-shu-de-hou-xu-bian-6

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 千羽的编程时光 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 剑指 Offer 26. 二叉搜索树的后序遍历序列
  • 方法一:递归
  • 方法二:栈
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档