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

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

例如:
给定二叉树: [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

哦。。。又是一道没有做出来的题。。。直接跟着大神的思路写一遍吧

解题思路:

  • 后序遍历定义: [ 左子树 | 右子树 | 根节点 ] ,即遍历顺序为 “左、右、根” 。
  • 二叉搜索树定义: 左子树中所有节点的值 < < < 根节点的值;右子树中所有节点的值 > > > 根节点的值;其左、右子树也分别为二叉搜索树。
    在这里插入图片描述
    方法一:递归分治
  • 根据二叉搜索树的定义,可以通过递归,判断所有子树的 正确性 (即其后序遍历是否满足二叉搜索树的定义) ,若所有子树都正确,则此序列为二叉搜索树的后序遍历。

递归解析:

  • 终止条件: i ≥ j i \geq j ij ,说明此子树节点数量 ≤ 1 \leq 1 1 ,无需判别正确性,因此直接返回 t r u e true true
  • 递推工作:
    1. 划分左右子树: 遍历后序遍历的 [ i , j ] [i, j] [i,j] 区间元素,寻找 第一个大于根节点 的节点,索引记为 m m m 。此时,可划分出左子树区间 [ i , m − 1 ] [i,m-1] [i,m1] 、右子树区间 [ m , j − 1 ] [m, j - 1] [m,j1] 、根节点索引 j j j
    2. 判断是否为二叉搜索树:
      • 左子树区间 [ i , m − 1 ] [i, m - 1] [i,m1] 内的所有节点都应 < p o s t o r d e r [ j ] < postorder[j] <postorder[j]。而第 1.划分左右子树 步骤已经保证左子树区间的正确性,因此只需要判断右子树区间即可。
      • 右子树区间 [ m , j − 1 ] [m, j-1] [m,j1] 内的所有节点都应 > p o s t o r d e r [ j ] > postorder[j] >postorder[j] 。实现方式为遍历,当遇到 ≤ p o s t o r d e r [ j ] \leq postorder[j] postorder[j] 的节点则跳出;则可通过 p = j p = j p=j 判断是否为二叉搜索树。
  • 返回值: 所有子树都需正确才可判定正确,因此使用 与逻辑符 and 连接。
    1. p = j p = j p=j : 判断 此树 是否正确。
    2. r e c u r ( i , m − 1 ) recur(i,m−1) recur(i,m1) : 判断 此树的左子树 是否正确。
    3. r e c u r ( m , j − 1 ) recur(m,j−1) recur(m,j1) : 判断 此树的右子树 是否正确。
class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        def recur(left, root):
            result = True
            if left >= root:
                return True

            right_root = root
            for i in range(left, root):
                if postorder[i] > postorder[root]:
                    right_root = i
                    break

            for i in range(right_root, root):
                if postorder[i] < postorder[root]:
                    result = False
                    break
            
            return result and recur(left, right_root - 1) and recur(right_root,root - 1 )

        return recur(0, len(postorder) - 1)

方法二:辅助单调栈

  • 后序遍历倒序: [ 根节点 | 右子树 | 左子树 ] 。类似 先序遍历的镜像 ,即先序遍历为 “根、左、右” 的顺序,而后序遍历的倒序为 “根、右、左” 顺序。

在这里插入图片描述

  • 设后序遍历倒序列表为 [ r n , r n − 1 , . . . , r 1 ] [r_{n}, r_{n-1},...,r_1] [rn,rn1,...,r1] ,遍历此列表,设索引为 i i i ,若为 二叉搜索树 ,则有:
    • 当节点值 r i > r i + 1 r_i > r_{i+1} ri>ri+1 时: 节点 r i r_i ri 一定是节点 r i + 1 r_{i+1} ri+1 的右子节点。
    • 当节点值 r i < r i + 1 r_i < r_{i+1} ri<ri+1 时: 节点 r i r_i ri 一定是某节点 r o o t root root 的左子节点,且 r o o t root root 为节点 r i + 1 , r i + 2 , . . . , r n r_{i+1}, r_{i+2},..., r_{n} ri+1,ri+2,...,rn 中值大于且最接近 r i r_i ri 的节点(∵ r o o t root root 直接连接 左子节点 r i r_i ri)。
  • 当遍历时遇到递减节点 r i < r i + 1 r_i < r_{i+1} ri<ri+1 ,若为二叉搜索树,则对于后序遍历中节点 r i r_i ri 右边的任意节点 r x ∈ [ r i − 1 , r i − 2 , . . . , r 1 ] r_x \in [r_{i-1}, r_{i-2}, ..., r_1] rx[ri1,ri2,...,r1] ,必有节点值 r x < r o o t r_x < root rx<root

节点 r x r_x rx 只可能为以下两种情况:① r x r_x rx r i r_i ri 的左、右子树的各节点;② r x r_x rx r o o t root root 的父节点或更高层父节点的左子树的各节点。在二叉搜索树中,以上节点都应小于 r o o t root root

在这里插入图片描述

  • 遍历 “后序遍历的倒序” 会多次遇到递减节点 r i r_i ri ,若所有的递减节点 r i r_i ri 对应的父节点 r o o t root root 都满足以上条件,则可判定为二叉搜索树。
  • 根据以上特点,考虑借助 单调栈 实现:
    1. 借助一个单调栈 s t a c k stack stack 存储值递增的节点;
    2. 每当遇到值递减的节点 r i r_i ri ,则通过出栈来更新节点 r i r_i ri 的父节点 r o o t root root
    3. 每轮判断 r i r_i ri r o o t root root 的值关系:
      1. r i > r o o t r_i > root ri>root 则说明不满足二叉搜索树定义,直接返回 f a l s e false false
      2. r i < r o o t r_i < root ri<root 则说明满足二叉搜索树定义,则继续遍历。

算法流程:

  1. 初始化: 单调栈 s t a c k stack stack ,父节点值 r o o t = + ∞ root = +\infin root=+ (初始值为正无穷大,可把树的根节点看为此无穷大节点的左孩子);
  2. 倒序遍历 p o s t o r d e r postorder postorder:记每个节点为 r i r_i ri
    1. 判断: r i > r o o t r_i>root ri>root ,说明此后序遍历序列不满足二叉搜索树定义,直接返回 f a l s e false false
    2. 更新父节点 r o o t root root : 当栈不为空 r i < s t a c k . p e e k ( ) r_i<stack.peek() ri<stack.peek() 时,循环执行出栈,并将出栈节点赋给 r o o t root root
    3. 入栈: 将当前节点 r i r_i ri入栈;
  3. 若遍历完成,则说明后序遍历满足二叉搜索树定义,返回 t r u e true true
class Solution:
    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
            while(stack and postorder[i] < stack[-1]):
                root = stack.pop()
            stack.append(postorder[i])
        return True

第二种解法目前没有看懂,仅作记录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值