--- tags: [笔试, 微派, 树, 二叉搜索树, DP, Go] create time: 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 结构 ```mermaid 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) 的单调栈解法: ```go 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,思路有什么变化? > > **答案**: 前序的第一个元素是根,然后找第一个大于根的元素为分界点,剩余部分应该全部大于根。验证通过后递归处理左右子树。也可以用类似的单调栈方法(正向遍历)。