题目描述

牛客原题: ✅ 补充题 133. 字典序最大的出栈序列

给你一个由 1 到 n 组成的排列 nums。所有元素必须按给定顺序进入同一个栈,但你可以在任意合法时刻弹出栈顶元素。

请返回所有合法出栈序列中字典序最大的一个。整个过程只允许入栈和出栈两种操作。

示例 1:

输入: nums = [2,1,3]
输出: [3,1,2]
解释: 先依次入栈 2、1、3,再依次出栈。输出 3 后,1 在 2 上方,因此不能得到 [3,2,1]。

示例 2:

输入: nums = [3,1,2]
输出: [3,2,1]
解释: 3 入栈后立即出栈;再将 1、2 入栈,依次弹出 2、1。

提示:

  • 1 <= n <= 5 * 10^4
  • 排列中的值都满足 1 <= val <= n

题意分析

字典序由第一个不同的输出位置决定,所以应让当前能合法输出的下一项尽量大。可选择的是立即弹出栈顶,或继续按输入顺序压栈;栈内更深元素被栈顶挡住,不能直接取出。

解法:后缀最大值决定栈顶何时输出

核心思路

[!blue]

suffix[i] 表示从 i 开始的剩余输入最大值。每次压入 nums[i] 后,比较栈顶与 suffix[i+1]:若未来存在更大的数,可以一直压入到它出现,使下一项更大,因此应等待。

若栈顶比未来所有数都大,继续压入只会让较小元素挡在它上方,不能改善当前输出;立即弹出是最优选择。弹出后对新栈顶重复判断,逐项固定字典序最优的前缀。

输入是 1~n 的排列,值均为正且不重复,因此用 0 表示空后缀最大值。最后一次压栈后所有剩余栈顶都大于 0,都会被弹出,不会遗留元素。

解题步骤

  1. 逆序求尚未入栈元素的后缀最大值。
  2. 按输入次序压栈。
  3. 栈顶大于剩余输入的最大值时持续出栈,得到字典序最大结果。

代码实现

class Solution {
    public int[] stackOrder(int[] nums) {
        int n = nums.length;
        int[] suffix = new int[n + 1];
        int[] stack = new int[n];
        int[] result = new int[n];

        for (int i = n - 1; i >= 0; i--) {
            suffix[i] = Math.max(nums[i], suffix[i + 1]);
        }

        int size = 0;
        int write = 0;

        for (int i = 0; i < n; i++) {
            stack[size++] = nums[i];

            while (size > 0 && stack[size - 1] > suffix[i + 1]) {
                result[write++] = stack[--size];
            }
        }

        return result;
    }
}
func stackOrder(nums []int) []int {
    n := len(nums)
    suffix := make([]int, n+1)
    for i := n - 1; i >= 0; i-- {
        suffix[i] = max(nums[i], suffix[i+1])
    }
    stack := make([]int, 0, n)
    result := make([]int, 0, n)
    for i, x := range nums {
        stack = append(stack, x)
        for len(stack) > 0 && stack[len(stack)-1] > suffix[i+1] {
            result = append(result, stack[len(stack)-1])
            stack = stack[:len(stack)-1]
        }
    }
    return result
}

复杂度分析

  • 时间复杂度:$O(n)$。
  • 空间复杂度:额外空间 $O(n)$。

关键点总结

[!green]

每一位都尽量先输出仍可取得的最大值,且不打乱输入次序;栈顶不满足条件时不能跳过它输出更深的值。

易错点总结

[!yellow]

输入是1…n的排列,末尾最大值哨兵可以取0;不能直接排序结果,也不能引入第二个辅助栈重排。

相似题目

题目 难度 关联与区别
946. 验证栈序列 中等 原题给定出栈序列验证合法性,本题在合法操作中主动选择字典序最大的结果。
2434. 使用机器人打印字典序最小的字符串 中等 同样依据尚未入栈的极值决定何时弹出;原题求字典序最小且允许重复字符,本题求最大且输入为排列。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/13685926
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!