Files
leetcode-go/笔试/微派 Test1/12-单选题10-树后序遍历.md
T

4.6 KiB
Raw Blame History

tags, create time
tags create time
笔试
微派
树
二叉搜索树
DP
Go
2026-05-16 15:30

12 - 单选题 10:判断是否为 BST 的后序遍历

题目

给定一个整数数组,判断它是否是一个二叉搜索树(BST)的后序遍历结果。例如 [1, 3, 2, 5, 7, 6, 4] 是否是某个 BST 的后序遍历?

选项 内容
A 是,因为数组中数字不重复且可以构成 BST
B 否,因为后序遍历不能唯一确定一棵 BST
C 是,符合 BST 后序遍历的规则:右子树根之前的所有节点分为两组——小于根的在左子树侧,大于根的在右子树侧
D 无法判断,需要同时给出前序和中序才能判断
点击查看答案与解析

✅ 正确答案:C


详细解析

BST 后序遍历的特性

后序遍历顺序: [左子树] → [右子树] → [根]
              └───── a ─────┘└───── b ─────┘└─ root ─┘
              
BST 性质: 左子树所有节点 < root < 右子树所有节点

所以合法的 BST 后序遍历必须满足:
存在分界点 k,使得:
  · a[0...k-1] < root  (全部小于根 → 左子树)
  · a[k...n-2] > root  (全部大于根 → 右子树)

对 [1, 3, 2, 5, 7, 6, 4] 的分析

根 = 4(最后一个元素)

扫描前面: 1, 3, 2, 5, 7, 6
         ↑ ↑ ↑   ↑ ↑ ↑
         左 左 左   右 右 右

左子树部分 [1, 3, 2] → 全部 < 4 ✓
右子树部分 [5, 7, 6] → 全部 > 4 ✓

继续递归:
  左子树: [1, 3, 2], 根=2
    [1] < 2 ✓, [] > 2 ✓ → 合法
  
  右子树: [5, 7, 6], 根=6
    [5] < 6 ✓, [7] > 6 ✓ → 合法

结论: **是**合法的 BST 后序遍历 ✓

对应的 BST 结构

graph TD
    R["4"] --> L["2"]
    R --> RR["6"]
    L --> LL["1"]
    L --> LR["3"]
    RR --> RL["5"]
    RR --> RRR["7"]
    
    style R fill:#a8e6cf
        4
       / \
      2    6
     / \  / \
    1   3 5   7

后序: 1, 3, 2, 5, 7, 6, 4 ✓

反例分析

如果输入是 [1, 5, 3, 4, 2]:

根 = 2
前面: [1, 5, 3, 4]
  1 < 2 ← 应该是左子树
  5 > 2 ← 但这是右子树的数
  3 > 2 ← 右子树
  4 > 2 ← 右子树

问题: 左子树区出现了值 1 之后紧接着出现了 5, 3, 4
  而 5 > 2(不在左子树区间内),3 > 2,4 > 2
  但这看起来合法? 等等...
  
再仔细看: 1 < 2 ✓, 但后面的 5, 3, 4 都 > 2 ✓
→ 这个也是合法的!

那换一个反例: [2, 1, 4, 3, 5]
根 = 5
  [2, 1, 4, 3]: 全部 < 5 ✓ → 左子树
  没有右子树部分 ✓

递归 [2, 1, 4, 3], 根 = 3
  [2, 1]: 全部 < 3 ✓ → 左子树
  [4]: > 3 ✓ → 右子树 ✓

看起来也合法...

真正的反例: [3, 1, 4, 2, 5]
根 = 5
  [3, 1, 4, 2]: 全部 < 5 ✓

递归 [3, 1, 4, 2], 根 = 2
  [3]: > 2 ✗ 应该全 ≤ 2,但 3 > 2
  然而 1 < 2
   
出现矛盾: 左侧区域混入了 > 2 的值 3
→ 不合法! ❌

O(n) 单调栈解法(进阶)⭐

虽然递归 O(n²) 也能解题,面试中常要求 O(n) 的单调栈解法:

func verifyPostorder(postorder []int) bool {
    stack := make([]int, 0, len(postorder))
    root := math.MaxInt64 // 代表"当前应该在右子树区域"的下界
    
    for i := len(postorder) - 1; i >= 0; i-- {
        // 如果当前值 < root,说明它出现在某个祖先节点的左子树中
        // 但后续元素都必须 <= 那个祖先节点的左孩子
        if postorder[i] > root {
            return false
        }
        
        // 从栈顶弹出比当前值大的元素
        for len(stack) > 0 && stack[len(stack)-1] > postorder[i] {
            root = stack[len(stack)-1] // 记录弹出的最大值作为下界
            stack = stack[:len(stack)-1]
        }
        
        stack = append(stack, postorder[i])
    }
    
    return true
}

[!tip] 核心逻辑 从后往前遍历,相当于反向后序(根 → 右 → 左)。用一个栈维护路径,当遇到下降趋势时弹出,弹出的最后一个值就是当前节点的祖先下界,后面不能再出现比这个下界大的值。

复杂度对比

方法 时间 空间
递归分治 O(n²) 最坏, O(n log n) 平均 O(h) = O(n) 最坏
单调栈 O(n) O(n)

[!question] 💡 思考题 如果是前序遍历判定 BST,思路有什么变化?

答案: 前序的第一个元素是根,然后找第一个大于根的元素为分界点,剩余部分应该全部大于根。验证通过后递归处理左右子树。也可以用类似的单调栈方法(正向遍历)。