剑指offer-23 -- 二叉搜索树的后序遍历序列 - C++

题目描述

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

方法一:递归法
大家普遍使用的是递归分割法,递归简单易懂容易实现,先来一次遍历以确定出左右子树的分界点,然后再分别对两棵子树进行递归判断。现在让我们来分析一下递归方法的时间复杂度:

以标准的完美二叉搜索树为例,递归的每一层都涉及到对序列的遍历,虽然层数越深节点越少(少了子树的根节点),但是这种减少是微不足道的,即使是到了最底层,依旧有n/2的节点(完美二叉树第i层节点数是其上所有节点数之和+1),因此递归方法在每一层的遍历开销是O(n),而对于二叉树而言,递归的层数平均是O(logn),因此,递归方法的最终复杂度是O(nlogn)

class Solution {
public:
    bool isBST(vector<int> &sequence){
        int len  = sequence.size();
        if(len == 1 || len == 0)
            return true;
        vector<int> left, right;
        int i = 0;
        while(sequence[i] < sequence[len - 1] && i < len - 1)
            left.push_back(sequence[i++]);
        while(sequence[i] > sequence[len - 1] && i < len - 1)
            right.push_back(sequence[i++]);
        if(i < len - 1)
            return false;
        return isBST(left) && isBST(right);
    }
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.size() == 0)
            return false;
        return isBST(sequence);
    }
};

方法二:上限约束法
那么贪婪的我们不禁就会想,有没有更好的方法??O(n)、甚至O(logn)?

由于一个正确的遍历序列,我们可以在它任意一个位置故意篡改,因此势必要遍历所有的元素才能确定它的正确性,所以个人认为,这个问题的时间复杂度下界应该就是O(n)。

具体来说,二叉搜索树的关键特征是:对于任意一棵子树,均有“左子树<根节点<右子树”,因此,它的根节点约束了它左右子树的取值范围,二叉搜索树的根root是它左子树值的上限(max),同时是它右子树值的下限(min)。如果我们从根节点出发往下走,那么高层祖辈节点序列就会不停地对低层未遍历节点形成一个上下限约束,只要低层节点没有违背这个约束,那么它就是合法的,否则,序列就是不合法的。

因为后序遍历的一些特性,我们可以从右到左倒序访问给定的序列,因为后序遍历的最后一个元素是根节点,倒序访问就相当于是从根到叶,从右到左的访问顺序,从根到叶可以让我们利用祖辈节点提供的上下限约束信息来判断孩子节点合法性,具体步骤如下:

  • 如果当前元素 > 上一个元素,这说明当前元素可能是上一个元素的右孩子,这时候:
    • 如果当前元素突破max上限约束,说明祖辈中存在 “左子树 > 根” 的情况,违背了搜索树的定义(尝试把图中4的值换成7,那么就有子辈7 > 祖辈max约束5,搜索树不成立);
    • 否则,当前元素就是上一个元素的右孩子,当前元素将成为新的祖辈节点,为后续节点提供约束;
  • 如果当前元素 < 上一个元素,这就说明当前元素是某个祖辈节点的左孩子,这时候:
    • 我们需要找出这个祖辈节点,并将该祖辈的右子树节点全部丢弃(因为它的右子树结构已经确定,无法为后续节点提供帮助),该祖辈节点的值将成为新的max上限约束,
    • 当前元素将成为新的祖辈节点,继续为后续节点提供约束;

因此,我们需要使用一个栈来存储已经确定了的祖辈节点,当新节点的合法性被确定时,入栈,当算法需要寻找当前节点是谁的左孩子时,出栈;

复杂度分析:在没有出栈的情况下,序列依次从右到左访问,信息依次入栈,每次也只访问栈顶的值,复杂度为O(n);在需要出栈的情况下,由于同一个节点不会反复入栈出栈,所以,最坏情况就是所有节点都要经历出栈操作,复杂度由O(n)变为O(2n)。忽略常数,算法的最终时间复杂度是O(n),空间复杂度也是O(n)

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.size() < 1)
            return false;
        stack<int> roots;
        roots.push(INT_MIN);
        int max = INT_MAX;
        for(int i=sequence.size()-1; i>=0; i--){
            if(sequence[i] > max)
                return false;
            while(sequence[i] < roots.top()){
                max = roots.top();
                roots.pop();
            }
            roots.push(sequence[i]);
        }
        return true;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值