• 剑指 Offer 33. 二叉搜索树的后序遍历序列(单调栈or递归)


    • 题目描述

    首先这里需要知道二叉搜索树的性质的先验知识

    二叉搜索树又叫二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树:

    • 若它的左子树不为空,则左子树上的所有结点都小于根节点上的值
    • 若它的右子树不为空,则右子树上的所有结点都大于根节点上的值
    • 它的左右子树也分别是二叉搜索树

    那么二叉搜索数的后续遍历一定有以下特性:

    • 所有的左子树均小于根节点
    • 所有右子树均大于根节点
    • 最后一个一定是根节点
    输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
    
     
    
    参考以下这颗二叉搜索树:
    
    5
    / 
    2 6
    / 
    1 3
    示例 1:
    
    输入: [1,6,3,2,5]
    输出: false
    示例 2:
    
    输入: [1,3,2,6,5]
    输出: true
     
    
    提示:
    
    数组长度 <= 1000
    • 题解一:递归解法参照剑指offer

    思路1:自己的解法

    首先需要判断左子树节点值小于根节点的值,然后判断右子树节点值大于根节点的值,然后用递归判断左子树是不是二叉搜索树,再递归判断右子树是否是二叉搜索树,最后将左右子树的判断结果bool值相与即可。

    class Solution:
        '''
        参考剑指offer书的解法,自己的实现
        '''
        def verifyPostorder(self, postorder):
            if not postorder:
                return True
            root = postorder[len(postorder)-1]
            left = True
            for i in range(len(postorder)):
                if root < postorder[i]:
                    break
            left_tree = postorder[:i]
            for j in range(i,len(postorder)-1):
                if postorder[j] < root:
                    return False
            if len(left_tree) > 1: #判断左子树
                left = self.verifyPostorder(left_tree)
            elif len(left_tree) == 1:
                if left_tree[0] > root:
                    return False
            right = True
            right_tree = postorder[i:len(postorder)-1]
            if len(right_tree) > 1: #判断右子树
                right = self.verifyPostorder(right_tree)
            return(left & right)

    思路二:递归+分治

    跟上面思路一差不多,都是判断左子树所有节点均小于根节点,右子树左右节点均大于根节点,然后返回了3个bool值,当这三个均为True,这棵树才是二叉搜索树

        def verifyPostorder(self, postorder: [int]) -> bool:
            def recur(i, j): #i j为左右边界
                if i >= j: return True #此时根节点数量小于等于1,无需判别
                p = i #如果左子树符合的话,p则指向左子树的和右子树的分界点
                while postorder[p] < postorder[j]: p += 1
                m = p
                #此时判断p == j如果右子树都比根节点大的话,此时p则会指向根节点的位置
                while postorder[p] > postorder[j]: p += 1
                #左子树区间(i ,m-1),右子树区间(m, j-1)
                return p == j and recur(i, m - 1) and recur(m, j - 1) #递归
    
            return recur(0, len(postorder) - 1)    
    • 题解二:单调递增栈,逆向遍历数组

    由于二叉搜索树是left <root < right的,后续遍历则是left-right-root,为了保证单调性,可以将后续遍历倒过来,则变成root->right->left。

    翻转先序遍历是root->right->left的,基于这样的性质和遍历方式,我们知道越往右越大,这样,就可以构造一个单调递增的栈,来记录遍历的元素。

    用单调栈的原因是:

    往右子树遍历的过程,value是越来越大的,一旦出现了value小于栈顶元素value的时候,就表示要开始进入左子树了

    (如果不是,就应该继续进入右子树,否则不满足二叉搜索树的定义),但是这个左子树是从哪个节点开始的呢?

    单调栈帮我们记录了这些节点,只要栈顶元素还比当前节点大,就表示还是右子树,要移除。

    因为我们要找到这个左孩子节点直接连接的父节点,也就是找到这个子树的根,只要栈顶元素还大于当前节点,就要一直弹出,直到栈顶元素小于节点,或者栈为空。栈顶的上一个元素就是子树节点的根。

    接下来,数组继续往前遍历,之后的左子树的每个节点,都要比子树的根要小,才能满足二叉搜索树,否则就不是二叉搜索树。

        def verifyPostorder(self, postorder: [int]) -> bool:
            stack, root = [], float("+inf")
            for i in range(len(postorder) - 1, -1, -1):
                if postorder[i] > root: return False #左子树比root节点大,则不是二叉搜索树
                while (stack and postorder[i] < stack[-1]):
                    root = stack.pop() #root记录子树节点的根
                stack.append(postorder[i])
            return True
    •  单调栈

    单调栈,顾名思义就是栈内元素单调按照递增(递减)顺序排列的栈。

    • 单调递增栈:栈中数据出栈的序列为单调递增序列
    • 单调递减栈:栈中数据出栈的序列为单调递减序列

    现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空或入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。

    • 10入栈时,栈为空,直接入栈,栈内元素为10。

    • 3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。

    • 7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。

    • 4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。

    • 12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。

     参考链接:https://blog.csdn.net/lucky52529/article/details/89155694

    https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/solution/mian-shi-ti-33-er-cha-sou-suo-shu-de-hou-xu-bian-6/

    https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/solution/dan-diao-di-zeng-zhan-by-shi-huo-de-xia-tian/

  • 相关阅读:
    Java StringBuilder、基本类型的包装类
    立个Flag不学好PHP誓不罢休
    LAMP搭建 转
    CentOS使用yum源中自带的rpm包安装LAMP环境
    CentOS RedHat YUM 源扩展补充(32位、64位均有)
    解决phpmyadmin上传文件大小限制的配置方法
    lanmp v2.5一键安装包发布(包括lamp,lnmp,lnamp安装)
    图像处理 jpg png gif svg
    NAT模式/路由模式/全路由模式 (转)
    网页制作中绝对路径和相对路径的区别
  • 原文地址:https://www.cnblogs.com/yeshengCqupt/p/13444860.html
Copyright © 2020-2023  润新知