leetcode—二叉搜索树的后续遍历

二叉搜索树的后续遍历

题目来源: leetcode传送门

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

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

     5
    / \
   2   6
  / \
 1   3

示例1:

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

示例2:

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

提示:

数组长度 <= 1000

方法一:暴力求树的存在性

刚看到这题直接的想法就是:由于我们知道了后续遍历,即左-右-根的序列,而且又是二叉搜索树,所以中序遍历(左-根-右)的顺序就是后续遍历的的从小到大排序。为了方便,将后续遍历翻转,即得到了根-右-左的排序,记 lmr(left-mid-right)=中序遍历的序列,mrl=后续遍历的翻转。
这样有了lmr和mrl就可以唯一的确定一棵树了,所以只需要根据这两个序列能够构造出一棵二叉树,就可以知道后续序列是否是某个搜索二叉树的后续遍历了。
具体构造树的方法为:取mrl第一个值即为根,然后在lmr中找到根的值,左侧即为左子树,右侧为右子树,再从mrl中的第一个元素开始取lmr左子树的个数个元素,重复完成上述操作(即递归求解左子树的存在性),右子树也是同理。如果左右子树都存在那么,这棵树也就存在。要注意的是递归时lmr和mrl的元素必须一一对应。

代码如下:

vector<int> mrl;
vector<int> lmr;
bool check(int mrlbegin, int mrlend, int lmrbegin, int lmrend)
{
    if(mrlend == mrlbegin && lmrend == lmrbegin)
        return true;
    else if(mrlend - mrlbegin != lmrend - lmrbegin)
        return false;
    for(int i = mrlbegin; i <= mrlend; i ++)
    {
        bool flag = false;
        for(int j = lmrbegin; j <= lmrend; j ++)
        {
            if(mrl[i] == lmr[j])
            {
                flag = true;
                break;
            }
        }
        if(!flag)
            return false;
    }
    int root = mrl[mrlbegin];
    int ptr = lmrbegin;
    for(; ptr <= lmrend; ptr ++)
    {
        if(lmr[ptr] == root)
        {
            break;
        }
    }
    if(ptr > lmrend)
        return false;
    bool ans1 = true;
    bool ans2 = true;
    if(mrlbegin + 1 >= 0 && mrlbegin + 1 <= mrlbegin + lmrend - ptr && mrlbegin + lmrend - ptr < mrl.size() && ptr + 1 <= lmrend)
        ans1 = check(mrlbegin + 1, mrlbegin + lmrend - ptr, ptr + 1, lmrend);
    if(mrlbegin + ptr - lmrbegin + 1 >= 0 && mrlbegin + lmrend - ptr + 1 <= mrlend && lmrbegin <= ptr - 1 && ptr - 1 < lmr.size())
        ans2 = check(mrlbegin + lmrend - ptr + 1, mrlend, lmrbegin, ptr - 1);
    return ans1 && ans2;
}
bool verifyPostorder(vector<int>& postorder) {
    if(postorder.size() == 0)
        return true;
    mrl = vector<int>{};
    for(int i = postorder.size() - 1; i >= 0; i --)
        mrl.push_back(postorder[i]);
    lmr = postorder;
    sort(lmr.begin(), lmr.end());
    return check(0, mrl.size() - 1, 0, lmr.size() - 1);
}  

但是这样实在是太复杂了,我在做这道题时肯定不会这样做的(这个是我在提交后又写的,已经通过了测试,可以放心食用,但是时间复杂度太高了,还是建议看下面的解法)

方法二:单调栈

这个解法是看到题解中有用栈的方法去解的(但是没看具体操作方法),经过很长一段时间的思考,终于想到了这种用栈的解法,最后与题解对比,思路基本一致(方法名叫做单调栈,可能是与栈中的单调性有关),下面是具体做法。只要所有元素都遍历完,那么就说明此序列是一个二叉搜索树的后续遍历,返回true。

代码如下:

bool verifyPostorder(vector<int>& postorder) {
    stack<int> s;
    int rootval = 2147483647;
    int cur = postorder.size() - 1;
    if(postorder.size() == 0)
        return true;
    else
        s.push(postorder[cur]);
    cur --;
    while(cur >= 0)
    {
        if(postorder[cur] > rootval)
            return false;
        if(!s.empty())
        {
            if(postorder[cur] > s.top())
            {
                s.push(postorder[cur]);
                cur --;
            }
            else
            {
                while(!s.empty() && postorder[cur] < s.top())
                {
                    rootval = s.top();
                    s.pop();
                }
            }
        }
        else
        {
            s.push(postorder[cur]);
            cur --;
        }
    }
    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值