剑指offer面试题33(java版):二叉搜索树的后续遍历

welcome to my blog

剑指offer面试题33(java版):二叉搜索树的后续遍历

题目描述

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

第四次做; 核心: 1)找到根节点, 确定左右子树范围, 递归处理 2) 递归函数使用arr.length-1时最好先处理arr长度为0的情况, 否则会出现[0,-1]的区间 3)别单独判断左右子树是否存在了, 交给base case处理
//左子树的所有值都比rootVal小; 右子树的所有值都比rootVal大
//BST上的值没有重复的
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        //input check
        if(postorder==null || postorder.length==0)
            return true;
        //通用的技巧: arr.length-1时需要先处理arr长度为0的情况, 否则会出现[0,-1]的区间
        return core(postorder, 0, postorder.length-1);
    }
    //递归函数逻辑: 找出[left,right]上的根节点; 根据BST性质找出左子树范围(当前条件), 判断左子树是否是BST的后续遍历(新条件新递归); 找出右子树范围(当前条件), 判断右子树是否是BST的后续遍历(新条件新递归)
    private boolean core(int[] arr, int left, int right){
        //base case
        if(left>=right)
            return true;
        int rootVal = arr[right];
        //左右子树的分界, 确切点说是从左往右起的第一个大于rootVal的位置 或者是rootVal的位置
        int index=left;
        while(index<right && arr[index]<rootVal){
            index++;
        }
        //here, index==right || arr[index]>rootVal
        boolean res=false;
        //如果存在左子树的话; 别单独判断了, 不存在的话会触发base case, 返回true
        //判断左子树是否是BST的后续遍历
        res = core(arr, left, index-1);
        if(res==false)
            return false;
        //如果存在右子树; 别单独判断了, 不存在的话会触发base case, 返回true
        //判断右子树中的值是否都大于rootVal
        for(int i=index; i<right; i++){
            if(arr[i]<rootVal)
                return false;
        }
        res = core(arr, index, right-1);
        return res;
    }
}

笔记

如果面试题要求处理一棵二叉树的遍历序列,

  • 可以先找到二叉树的根节点
  • 再基于根节点把整棵树的遍历序列拆分成左子树对应的子序列和右子树对应的子序列
  • 接下来再递归地处理这两个子序列

思路

二叉搜索树的后序遍历结果:

  1. 最后一个数字是根节点的值;
  2. 靠前的部分数字是左子树节点的值,都比根节点的值小;
  3. 靠后的部分数字是右子树的值,都比根节点的值大
第三次做, 弄清楚base case出现的例子, 见注释; 利用二叉搜索树的性质:左<根<右; 后续遍历最后一个节点是根节点
/*
二叉搜索树的性质: 1)中序遍历结果是非递减的; 2)根比左子树所有的值都大, 比右子树所有的值都小
*/
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence==null || sequence.length==0)
            return false;
        boolean res = Core(sequence, 0, sequence.length-1);
        return res;
    }
    public boolean Core(int[] arr, int left, int right){
        //base case 必须是left>right或者Left>=right, 不能用left==right, 
        //例子:1,3; 此时root是3,leftEnd是3, 最后return中的右子树是Core(arr,1,0), left > right
        //例子:只有一个3
        //其实用left>=right最好,少点递归层数
        if(left>=right)
            return true;
        //
        int root = arr[right];
        //寻找左子树边界
        int leftEnd=left;
        while(arr[leftEnd] < root){
            leftEnd++;
        }
        //判断剩余部分是不是都大于root
        for(int i=leftEnd; i<right; i++){
            if(arr[i] < root)
                return false;
        }
        return Core(arr, left, leftEnd-1) && Core(arr, leftEnd, right-1);
    }
}
第二次做,还不是特别清晰,尤其是base case
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        /*
        通过后序遍历能够找到根节点
        配合二叉搜索树的性质:左子树所有节点值小于根; 右子树所有节点值大于根(这个条件方便使用递归)
        (额外提一下二叉搜索树的另一个性质:中序遍历结果是递增的;记住这两个常用的条件)
        */
        if(sequence==null || sequence.length==0)
            return false;
        if(sequence.length==1)
            return true;
        //
        boolean res = Core(sequence, 0, sequence.length-1);
        return res;
    }
    public boolean Core(int[] arr, int left, int right){
        //base case
        if(left>right) //vital
            return true;
        //
        boolean res = false;
        int rootVal = arr[right];
        int division = 0;
        //找出右子树第一个节点
        while(division < right && arr[division] < rootVal)
            division++;
        //判断右子树是否满足条件
        while(division<right){
            if(arr[division]<rootVal)
                return false;
            division++;
        }
        //左右子树也满足搜索二叉树的要求才返回true
        return Core(arr, left, division-1) && Core(arr, division, right-1);
    }
}
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        /*
        二叉搜索树的后序遍历结果: 
            1.最后一个数字是根节点的值; 
            2.靠前的部分数字是左子树节点的值,都比根节点的值小; 
            3.靠后的部分数字是右子树的值,都比根节点的值大
        */
        //input check
        if(sequence.length < 1)
            return false;
        //execute
        return VerifySequenceOfBSTCore(sequence, 0, sequence.length-1);
    }
    public boolean VerifySequenceOfBSTCore(int[] sequence, int start, int end){
        // recursion finish
        if(end - start <= 1)
            return true;
        // execute
        // 判断靠前的部分是否都比根节点的值小  //注意只有左子树或者只有右子树的情况
        int index = start;
        while(index < end){ //找出右子树第一个节点对应的索引index
            if(sequence[index] > sequence[end])
                break;
            index++;
        }
        //here: index >= end OR sequence[index]>sequence[end]
        int indexLatter = index;
        while(indexLatter < end){ // 判断是否满足后序遍历的特点: 右子树中的值都比根节点的值大
            if(sequence[indexLatter] < sequence[end])
                return false; // 只有这里有可能返回false
            indexLatter++;
        }
        // here: we have both the valid left subtree and the valid right subtree
        // start recursion
        // 左右子树都得满足性质才可以
        return VerifySequenceOfBSTCore(sequence, start, index-1) && VerifySequenceOfBSTCore(sequence, indexLatter, end);
    }
}
非递归的版本(优秀答案;C++)
  • 非递归
  • 非递归也是一个基于递归的思想:
  • 左子树一定比右子树小,因此去掉根后,数字分为left,right两部分,right部分的
  • 最后一个数字是右子树的根,它比左子树所有值大,因此我们可以每次只看右子树是否符合条件
  • 左子树怎么办? 即使到达了左子树,左子树也可以看成由左右子树组成的树, 还像处理右子树那样处理右子树
  • 对于左子树, 则继续看成由左右子树组成的树,如此循环判断…
  • 当判断到还剩sequence中还剩一个元素(或两个元素)时跳出循环
class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        int size = sequence.size();
        if(0==size)return false;
 
        int i = 0;
        while(--size) // size为0时跳出循环
        {
            while(sequence[i++]<sequence[size]);
            while(sequence[i++]>sequence[size]);
 
            if(i<size)return false;
            i=0;
        }
        return true;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值