剑指Offer 33、二叉搜索树的后序遍历序列

219 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情

题目:给定一个数组, 判断该数组是否为某个二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。本题有一个条件是输入数组元素互不相同。

解题思路

本题的考察点是希望用二叉树的特性来解决本题,首先二叉树具有一个特性是左子树的所有值都小于根节点,右子树的所有值都大于根节点。

根据这个特性,如果一个序列确实是一颗二叉树的后序遍历结果,那么我们可以确定的是根节点必然是序列的最后一个节点,此时我们需要确定的是这棵树的左子树和右子树,因为右子树的所有值都大于根节点,因此我们可以从序列的首元素位置开始寻找,找到第一个大于等于根节点的元素,则此元素即为右子树的起点。之后继续向后判断是否之后元素都大于根节点即可,如果都满足则递归判断左右子树,可得代码如下:

public static boolean verifyPostorder(int[] postorder) {
    int len = postorder.length;
    if(len<2) return true;
    return verify(postorder, 0, len-1);
}

public static boolean verify(int[] postorder, int cur, int max){
    if(cur>=max) return true;
    // 首先得到根节点
    int root = postorder[max];

    // 之后得到左右子树的区域
    int right = cur;

    for(int i=cur;i<=max;i++){
        if(postorder[i]>=root) {
            right = i;
            break;
        }
    }
    // while (right < max && postorder[right] < root){ // 从当前区域找到第一个大于根节点的,说明后续区域数值都在右子树中
    //   right++;
    // }

    // 此时左子树的范围(cur---right-1)
    // 右子树的范围(right---root-1)
    // 查找左子树是否都小于根(根据上方此步可跳过),右子树是否都大于根

    for(int i=right;i<max;i++){
        if(postorder[i]<root) return false;
    }

    // 递归检查左右子树
    return verify(postorder, cur, right-1)&&verify(postorder, right, max-1);
}

本题还有一个有意思的解法是单调栈。首先明白序列的最后一个节点的值必然是根节点,那么往前如果比根节点大则必然是右子树,如果比此值小则必然是左子树,具体代码如下,可以自己模拟体会一下:

public boolean verifyPostorder(int[] postorder) {
    int len = postorder.length;
    if(len<2) return true;

    int postValue = Integer.MAX_VALUE;
    ArrayDeque<Integer> stack = new ArrayDeque<>();
    for(int i=len-1;i>=0;i--){
        if(postorder[i]>postValue) return false;

        while(!stack.isEmpty()&&postorder[i]<stack.peek()){
            postValue = stack.pop();
        }
        stack.push(postorder[i]);
    }
    return true;
}