【动画算法】二叉搜索树的后序遍历序列

WXGZH

每日一道算法 欢迎关注 WXGZH:小夕学算法

题目

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

参考以下这颗二叉搜索树:

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

  • 运用递归的思路来不断的判断子树是否是一颗二叉搜索树,运用二叉搜索树的性质:左子树的节点值比根节点的小,右子树的节点值比根节点的大;在一个后续遍历的序列中,位于序列最后的元素是根节点
  • 所以可以总结出一个后序序列可划分成['左子树','右子树','根节点']这三部分组成,顺序是严格的
    好了,有了上面的特性,我们使用一个例子进行分析:


  • 划分左子树,我们能知道现在这个树的根节点为[5],从头开始遍历节点,直到我们遇到比根节点大的或者根节点,之前那些比根节点小的值是左子树的节点,此树的左子树序列为[1]
  • 划分右子树,可以看到当遍历到6的时候,6>5,按照上面总结的规律,说明6之后到根节点的序列都应该是右子树,即[6,3,2],这是就需要判断下是否在右子树的序列中存在比根节点小的元素,如果有的话就说明是不符合条件的,显然[3,2]都是比[5]要小的,不符合条件
  • 如果不存在在右子树中比根节点小的元素,递归的遍历上述左右子树







代码

Java

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        return recur(postorder, 0, postorder.length - 1);
    }

    public boolean recur(int [] postorder, int i, int j) {
        if (i >= j) return true;
        int m = i;
        while(postorder[i] < postorder[j]) i++; // 小于j的都是左子树 j是根节点 根据二叉搜索树的性质
        int p = i; // 有可能是右子树开头的第一个节点
        while(postorder[i] > postorder[j]) i++;
        if (i != j) return false; // 如果是正常的后序遍历序列  必然相等
        return recur(postorder, m , p -1) && recur(postorder, p , j - 1);
    }
}

C++

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        if(postorder.empty()) return true;
        int len = postorder.size();
        return recursion(postorder, 0, len - 1);
    }
    bool recursion(vector<int>& postorder, int left, int right)
    {
        if(left >= right) return true; // 没有左子树(比如[5,4,3,2,1]),true
        int root = postorder[right];
        int pos = left; // 必须要设置一个 pos 变量保存 left,不能直接操作 left!
        for(; pos<right; ++pos)
        {
            // 找到右子树起点
            if(postorder[pos] > root) break;
        }
        for(int j=pos; j<right; ++j)
        {
            // 如果右子树部分有小于 root 的,false
            if(postorder[j] < root) return false;
        }
        // 看左子树是否也是后序遍历。如果前面不设置 pos 的话,这里就会是 (postorder,0,left-1),会严重超时
        if(!recursion(postorder, left, pos - 1)) return false; 
        if(!recursion(postorder, pos, right - 1)) return false;// 看右子树是否也是后序遍历
        return true;
    }
};

Python

class Solution(object):
    def verifyPostorder(self, postorder):
        """
        :type postorder: List[int]
        :rtype: bool
        """
        if not postorder:
            return True
        def isTree(postorder):
            root = postorder[-1]
            length = len(postorder)
            for i in range(length): # 找到左子树的区间,此时注意下这样的切分不可能出现左子树中的节点比根节点大
                if postorder[i] > root:
                    break
            
            for j in range(i, length-1):# 如果右子树中存在比根节点的小的值,那么是不符合条件的
                if postorder[j] < root:
                    return False

            left = True
            if i > 0:
                left = isTree(postorder[:i])#判断左子树是否符合条件
            right = True
            if i < length -1 :
                right = isTree(postorder[i:-1])#判断右子树是否符合条件
            return left and right
        return isTree(postorder)

JS

/**
 * @param {number[]} postorder
 * @return {boolean}
 */
var verifyPostorder = function (postorder) {
    let len = postorder.length;
    // 若为叶子节点,则返回 true
    if (len < 2) return true
    // 后序遍历的最后一个元素为根节点
    let root = postorder[len - 1];
    let i = 0
    // 划分左/右子树
    for (; i < len - 1; i++) {
        if (postorder[i] > root) break
    }
    // 判断右子树中的元素是否都大于 root,此处用到 every (数组 API,数组的每个元素都返回 true 则整体返回 true)
    let result = postorder.slice(i, len - 1).every(x => x > root);
    if (result) {
        // 对左右子树进行递归调用,左右子树通过 i 进行分割
        return verifyPostorder(postorder.slice(0, i)) && verifyPostorder(postorder.slice(i, len - 1))
    } else {
        return false
    }
};

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容