[题解]剑指 Offer 33. 二叉搜索树的后序遍历序列(C++)

题目

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

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

     5
    / \
   2   6
  / \
 1   3

示例 1:

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

示例 2:

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

提示:

  1. 数组长度 <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

二叉搜索树满足左子树元素小于根节点元素小于右子树元素的条件,同时后序遍历得到的节点顺序是左子树->右子树->根节点。利用分治的思想,先确定根节点(数组结尾处元素),然后确定左子树和右子树,如果发现左子树中有节点的元素大于根节点元素或者右子树中有节点的元素小于根节点元素,就返回false;没有就继续对左子树和右子树进行判断,并将左子树和右子树的递归判断结果作为判定依据返回。
时间复杂度O(n2),空间复杂度O(n)。

代码

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        int n = postorder.size();
        return verifyPostorder(postorder, 0, n - 1);
    }

private:
    bool verifyPostorder(vector<int>& postorder, int l, int r)
    {
        if(l >= r) return true;
        int pos = l;
        while(postorder[pos] < postorder[r]) ++pos;
        int m = pos;
        while(postorder[pos] > postorder[r]) ++pos;
        return pos == r && verifyPostorder(postorder, l, m - 1) && verifyPostorder(postorder, m, r - 1);
    }
};

改进

这题还有一种思路是根据单调栈来做,以题中的二叉树为例,其后序遍历为[1,3,2,6,5],若将其翻转,即为[5,6,2,3,1]。翻转后的数组arr有两个特点:

  • 若arr[i] < arr[i + 1],则arr[i + 1]必定是arr[i]的右子节点。为什么?从未翻转的数组里看就能知道,后序遍历最后遍历到根节点,那么一个节点的右子节点必定出现在其左边第一个位置,因为遍历顺序是:左子树->右子树的左子树->右子树的右子树->右子树的根(即根的右子节点)->根。那么在翻转后的数组中,下一个元素比当前元素大,必定是其右子节点。
  • 若arr[i] > arr[i + 1],则arr[i + 1]一定是下标从0到i中比arr[i + 1]大的元素中最小的那个。听起来可能有点绕,但是分析一下就很清晰:出现了降序排列,说明此时刚好从右子树的遍历进入了左子树的遍历(因为翻转之后是从根->右子树->左子树这样的顺序来的,而右子树的值全部比根大,对于子树这话也成立)。既然出现了左子树,说明根已经在前面出现过了,而且是前面的遍历结果里比当前节点值大的节点中最小的那个(其他的都是右子树)。

基于上面的特点,我们用一个单调栈记录遍历到的元素,当数组递增时一直入栈,因为这时我们在向右子树一直深入;一旦遇到数组元素比栈顶元素小,就需要开始抛出元素,注意我们这时在右子树里面,所以抛出元素时记录抛出的栈顶元素,直到我们遇到栈顶元素比当前元素小,这时说明上一个抛出的元素就是当前元素的父节点,而我们刚刚进入其左子树!把上一个元素记下来作为临时根节点,我们继续向后遍历数组,一旦发现有元素比临时根大(在二叉搜索树中这是不可能的,因为比临时根节点大的都是其右子树上的元素,而这些元素已经被我们抛掉了)就返回false;如果又遇到降序排列,就重复之前出栈的操作。遍历完整个数组之后返回true。
时间复杂度O(n),空间复杂度O(n)。

代码

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        stack<int> stk;
        int root = INT_MAX;
        
        for(int i = postorder.size() - 1; i >= 0; --i)
        {
            if(postorder[i] > root) return false;
            while(!stk.empty() && postorder[i] < stk.top()){
                root = stk.top();
                stk.pop();
            }
            stk.push(postorder[i]);
        }
        
        return true;
    }
};
posted @   浮生的刹那  阅读(54)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
  1. 1 Night City r e l,Artemis Delta
  2. 2 Gold Steps(人生何处不青山) Neck Deep
  3. 3 Devil Trigger Ali Edwards
  4. 4 Hopeless Case Roam
  5. 5 On My Own Blitz Kids
  6. 6 I Really Want to Stay At Your House Rosa Walton & Hallie Coggins
  7. 7 Major Crimes Health & Window Weather
Night City - r e l,Artemis Delta
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.
点击右上角即可分享
微信分享提示