JZ23 二叉搜索树的后序遍历序列

JZ23 二叉搜索树的后序遍历序列

题目描述:

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

示例:

输入:
[4,8,6,12,16,14,10]

返回值:
true

题解:

方法一:递归法:

1.思路:

二叉树的后续遍历:先访问左节点,再访问右节点,最后访问根节点
所以一个后续遍历的数组,最后一位肯定是二叉树的根节点
何为二叉搜索树:左节点必定小于根节点,右节点必定小于根节点;左子树和右子树也是二叉搜索树

所以思路就来了:
我们可以直接拿到二叉树的根节点,然后找到其左子树的所有节点和右子树的所有节点,
然后切成两个二叉树,继续递归调用去做同样的判断。

2.复杂度:

时间复杂度:O(nlogn)
空间复杂度:O(n)

3.代码:

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
// 递归法:
        if(sequence == null || sequence.length == 0)
            return false;
        
        return helpFun(sequence,0, sequence.length-1);        
    }
    
    boolean helpFun(int [] sequence, int lift, int right){
        if(lift >= right)
            return true;
        
        int i = lift;
        
        for(; i<right; i++){
            if(sequence[i]>=sequence[right])
                break;
        }
        
        int j = i;
        for(; j<right; j++){
            if(sequence[j]<sequence[right])
                return false;
        }
        
        return helpFun(sequence,lift, i-1) & helpFun(sequence, i, right-1);
    }
}

方法二:非递归法:

1.思路:

最后一个是中间值,但是我们去掉最后一个,新的最后一个同样也满足我们说的规律!很有意思,所以就不需要递归,直接while就好了

2.复杂度:

时间复杂度:O(n^2)
空间复杂度:O(n)

3.代码:

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {        
// 非递归法:
        int size = sequence.length;
        if(0==size)
            return false;
        
        int i=0;
        while(--size >= 0){
            while(sequence[i] < sequence[size]) i++;
            while(sequence[i] > sequence[size]) i++;
            if(sequence[i] < sequence[size])
                return false;
            
            i=0;
        }
        return true;
    }

}

方法三:上限约束法:

这个方法是借鉴牛客题解

1.思路:

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

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

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

https://uploadfiles.nowcoder.com/images/20200613/709610362_1592040472826_5BFFAC2EDB6577A9D579A4583598D79C

2.复杂度:

时间复杂度:O(n)
空间复杂度:O(n)

由于后面贴出的代码使用了双重循环,所以可能让人迷惑,这里简单说明一下:内循环的执行总次数为pop的次数,而pop次数等于push次数(同一个节点不会反复入栈出栈),显然,push次数为n次,因此内循环的总执行次数也为n次,故时间复杂度为O(n),显然,空间复杂度也是O(n)。

3.代码:

import java.util.*;
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
// 上限约束法
        if(sequence.length<1)return false;
        // roots栈里面依次存放各层祖辈节点的值
        // 事先放入一个值以避免对空栈进行判断
        Stack<Integer> roots = new Stack<>();
        roots.push(Integer.MIN_VALUE);
        int max = Integer.MAX_VALUE;
        
        for(int i=sequence.length-1; i>=0; i--){
            if(sequence[i]>max) return false;
            // 如果节点小于roots的栈顶,说明该节点是某个祖辈的左孩子
            // 不断出栈,直到找出该祖辈,同时,该祖辈也提供了新的max约束
            while(sequence[i] < roots.peek()){
                max = roots.peek();
                roots.pop();
            }
            // 该节点成了新一代的祖辈节点,为后续节点判断自己的位置提供依据
            roots.push(sequence[i]);
        }
        return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值