题目要求

给定一个由 1 - n 组成的数组和一个栈,并按照数组的顺序入栈。

在不打乱入栈顺序的情况下,仅利用入栈和出栈两种操作,输出字典序最大的出栈序列

解题思路:贪心

要想输出的序列字典序尽可能大,必然用到贪心的思想,即每次出栈的数字尽可能大。具体思路如下:

1、出栈序列的第一个数字必然为n,我们按顺序入栈,直到n入栈时,将n出栈。

2、接下来我们要从尚未入栈的数字中找到其最大值maxi。循环比较maxi和栈顶元素的大小,如果栈顶元素更大,那么该元素出栈,直到栈顶元素小于maxi为止。

3、继续按顺序入栈,直到maxi入栈时,将maxi出栈。

4、重复步骤2和3,直到数组中所有数字都入栈为止。最后将栈中所有元素出栈,得到最终序列。

这个思路的难点在于如何获得每一个maxi。

动态规划

我们创建一个dp数组,让dp[i]表示数组中第i个元素开始到数组末尾之间的最大元素。

那么有动态转移方程 dp[i] = max(a[i], dp[i + 1])。

初始化时,dp[n - 1] = a[n - 1],然后让i从后到前遍历即可。

很显然,dp[i]就是每个位置i对应的maxi。

c++的set容器

set容器是一个有序的容器,其底层是红黑树,增删改查的时间复杂度都是O(logN)。

这一题我们可以用set容器来代替dp数组。

初始时将1 - n全部插入到set中,每当有元素入栈,则将该元素从set中删除。而每次获取的maxi,即为set中剩余元素的最大值,可以直接用迭代器rbegin得到。

c++代码

下面为使用set容器的代码。你可以尝试自己实现dp数组。

注意要防止越界,当set中容量为0时,可以将maxi设置为-1。

class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 栈排序
     * @param a int整型vector 描述入栈顺序
     * @return int整型vector
     */
    vector<int> solve(vector<int>& a) {
        int n = a.size();
        set<int> st;
        for (int i = 1; i <= n; ++i) st.insert(i);
        vector<int> ans;
        ans.reserve(n);
        stack<int> sk;
        int maxi = n;
        for (int& i : a) {
            st.erase(i);
            if (i == maxi) {
                ans.push_back(i);
                maxi = st.size() == 0 ? -1 : *st.rbegin();
                while (!sk.empty() && sk.top() >= maxi) {
                    ans.push_back(sk.top());
                    sk.pop();
                }
            } else {
                sk.push(i);
            }
        }
        return ans;
    }
};