剑指offer 33:二叉搜索树的后序遍历序列

题目描述:

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

数据范围: 节点数量0≤n≤1000 ,节点上的值满足1≤val≤105 ,保证节点上的值各不相同
要求:空间复杂度 O(n) ,时间时间复杂度 O(n^2)

提示:

1、二叉搜索树是指父亲节点大于左子树中的全部节点,但是小于右子树中的全部节点的树。

2、该题我们约定空树不是二叉搜索树

3、后序遍历是指按照 “左子树-右子树-根节点” 的顺序遍历

4、参考下面的二叉搜索树,示例 1:

示例1

输入:[1,3,2]

返回值:true

说明:是上图的后序遍历 ,返回true

示例2

输入:[3,1,2]

返回值:false

说明:不属于上图的后序遍历,从另外的二叉搜索树也不能后序遍历出该序列 ,因为最后的2一定是根节点,前面一定是孩子节点,可能是左孩子,右孩子,根节点,也可能是全左孩子,根节点,也可能是全右孩子,根节点,但是[3,1,2]的组合都不能满足这些情况,故返回false

示例3

输入:[5,7,6,9,11,10,8]

返回值:true

 解法一:递归

思路:

        题目给的是后序遍历数组,借助后序遍历的特点:左右根,可知数组中的最后一个元素是树的根节点。对于二叉搜索树来看,其满足左子树的所有数都小于根节点,右子树的所有数都大于根节点。根据此特性,可以以根节点为目标值,从起始位置到 len-1 的数组区间内找到左右子树的分界点。当左子树中出现大于根节点的数时,则表明此序列不合法。之后再通过分治的思想,对左右子树进行判断。 

1、找到根节点。

2、 从0到 len-1 的区间内从后向前遍历,找到第一个小于根节点的值,记为splitIndex。

3、以 splitIndex 将数组区间划分为两部分,判断左子树中若是有任一元素大于根结点的值,直接返回false。否则就继续进行递归,判断是否为合法的后序遍历序列。

代码:

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        // 处理特殊情况
        if(sequence == null || sequence.length == 0){
            return false;
        }
        return isLegal(sequence, 0, sequence.length-1);
    }
    // 判断是否合法
    public boolean isLegal(int[] array, int left, int right){
        if(left >= right){
            return true;
        }
        //根节点
        int root = array[right];
        //找到左右子树的分界点
        int splitIndex = right-1;
        while(splitIndex >= 0 && array[splitIndex] > root){
            splitIndex--;
        }
        //判断左子树是否合法
        for (int i = left; i <= splitIndex; i++) {
            if(array[i] > root){
                return false;
            }
        }
        //对左右子树进行合法性判断
        return isLegal(array, left, splitIndex) && isLegal(array, splitIndex+1, right-1);
    }
}

 解法二:栈

思路:

        这个思路是看到了别人的。对于二叉搜索树来说,其左孩子的值小于父节点,右孩子的值大于根节点,则中序遍历可得一个升序数组,且其中序遍历和后序遍历满足栈的压入和弹出。因此,可对其后序遍历的数组进行排序得到中序遍历的结果,然后再判断中序遍历和后序遍历是否满足栈的压入和弹出。

代码:

import java.util.Stack;
import java.util.Arrays;

public class Solution {
    public boolean VerifySquenceOfBST(int [] seq) {
        int[] arr = seq.clone();
        Arrays.sort(arr);
        return IsPopOrder(arr,seq);
        
    }
    
//判断第二个序列是否可能为第一个序列的弹出顺序,引用的“栈的压入、弹出序列”题目的答案
public boolean IsPopOrder(int [] pushA,int [] popA) {
        if(pushA.length == 0 || popA.length == 0)
            return false;
        Stack<Integer> s = new Stack<Integer>();
        //用于标识弹出序列的位置
        int popIndex = 0;
        for(int i = 0; i< pushA.length;i++){
            s.push(pushA[i]);
            //如果栈不为空,且栈顶元素等于弹出序列
            while(!s.empty() &&s.peek() == popA[popIndex]){
                //出栈
                s.pop();
                //弹出序列向后一位
                popIndex++;
            }
        }
        return s.empty();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值