剑指Offer-25-二叉搜索树的后序遍历序列

题目

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

解析

预备知识

后序遍历为:先访问左子树,后访问右子树,最后访问根节点。总结为:左右中。
二叉搜索树:左子树比根节点的值小,右子树比根节点的值大。左右子树亦如此。
例如如下图的二叉搜索树的后序遍历为:
这里写图片描述
后序遍历为:1327864

思路一

根据后序遍历的定义,我们可以发现序列的最后一个结点必为根节点。根节点之前的序列为左右子树序列。又根据二叉搜索树的定义,左子树的所有点都小于根节点,右子树的所有点都大于根节点。所以我们只需在除了根节点的序列中找到第一个大于根节点的节点,那么这个节点及其之后直到根节点之前的序列为右子树,序列开头到这个节点之前的序列则为左子树。
要想判断是否是合法的二叉搜索的后序遍历,那么我们必须保证左子树的所有的节点小于根节点,但是我们已经找右子树的根节点时已经保证了这个过程,所以我们现在的工作就剩下判断右子树的所有点是否大于根节点即可,若其中有小于根节点的值,那么说明不合法。
如果根节点的左右子树满足二叉搜索树的规定,那么接下来判断左右子树的遍历序列是否满足二叉搜索树的要求。判断左右子树其实可以看做原问题的子问题,所以这是一个完美的递归过程。

    /**
     * 主函数
     * @param sequence
     * @return
     */
    public static boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence == null || sequence.length == 0) {
            return false;
        }
        return VerifySquenceOfBST(sequence, 0, sequence.length - 1);
    }

    /**
     * 递归判断当前树结构的后序遍历是否满足二叉搜索
     * @param sequence
     * @param start
     * @param root
     * @return
     */
    public static boolean VerifySquenceOfBST(int[] sequence, int start, int root) {
        if(start >= root) {
            return true;
        }
        int indexOfRightRoot = start;
        //找到右子树的根节点
        while(indexOfRightRoot < root && sequence[indexOfRightRoot] <= sequence[root]) {
            indexOfRightRoot++;
        }
        //判断右子树是否全部大于根节点
        for(int i = indexOfRightRoot; i < root; i++) {
            if(sequence[i] < sequence[root]) {
                return false;
            }
        }
        //递归判断左右子树
        return VerifySquenceOfBST(sequence, start, indexOfRightRoot - 1)
                && VerifySquenceOfBST(sequence, indexOfRightRoot, root - 1);
    }

思路二

此为非递归实现,参考某大神的思路。
其实这也是利用递归划分子问题来做的。在思路一中我们已经的出了只需判断右子树是否合法即可。而后序遍历中左子树与右子树的左子树的序列是连着的,所以我们可以每次删除最后一个结点,即可得到右子树。不断缩短遍历序列,我们就能访问到这棵树的所有的右子树结构。只要都满足,那么总树必满足。为了说明该思路,我们举个例子:
比如开头那棵树,遍历序列为1327864。
1. 首先右子树为786,判断786是否大于4,是,继续
2. 删除最后一点4,那么当前序列中的右子树为78,判断78是否大于6,是,继续
3. 删除最后一点6,那么当前序列中的右子树为7,判断7是否大于8,是,继续
4. 删除最后一点8,没有右子树,继续
5. 删除最后一点7,没有右子树,继续
6. 删除最后一点2,那么当前序列中的右子树为3,判断3是否大于2,是,继续
7. 删除最后一点3,没有右子树,继续
8. 删除最后一1,结束遍历

    /**
     * 非递归实现
     * @param sequence
     * @return
     */
    public static boolean VerifySquenceOfBST2(int [] sequence) {
        if(sequence == null || sequence.length == 0) {
            return false;
        }
        int index = 0;
        int size = sequence.length;
        while(--size > 0) {
            //找右子树的第一个节点
            while(sequence[index] < sequence[size]) {
                index++;
            }
            //判断右子树是否满足规定
            while(index < size && sequence[index++] > sequence[size]);
            //若index小于size,说明右子树有个值小于根节点,说明不合法
            if(index < size) {
                return false;
            }
            index = 0;
        }
        return true;
    }

总结

关于二叉树遍历问题,我们首要问题就是要找到根节点,然后基于根节点拆分为左子树和右子树,然后递归处理左右子树即可。

猜你喜欢

转载自blog.csdn.net/dawn_after_dark/article/details/80955839