- 30.包括min函数的栈
 - 09.用两个栈实现队列
 - 剑指 Offer II 036. 后缀表达式(逆波兰式)">剑指 Offer II 036. 后缀表达式(逆波兰式)
 - 剑指 Offer II 037. 小行星碰撞">剑指 Offer II 037. 小行星碰撞
 - 单调栈
- 剑指 Offer II 038. 每日温度(单调栈)">剑指 Offer II 038. 每日温度(单调栈)
 - 剑指 Offer II 039. 直方图最大矩形面积(hard)">剑指 Offer II 039. 直方图最大矩形面积(hard)
 - 剑指 Offer II 040. 矩阵中最大的矩形(重点)">剑指 Offer II 040. 矩阵中最大的矩形(重点)
 
 
30.包括min函数的栈
核心在于用另一个栈来保存当前栈的最小值,并且另一个栈顶始终是最小值,顺序按照原始栈的顺序来。
class MinStack {public:stack<int> stack1, stack2;/** initialize your data structure here. */MinStack() {while(!stack1.empty()){stack1.pop();}while(!stack2.empty()){stack2.pop();}}void push(int x) {if(stack2.empty()||stack2.top() >= x){stack2.push(x);}stack1.push(x);}void pop() {if(stack1.top() == stack2.top()){stack2.pop();}stack1.pop();}int top() {return stack1.top();}int min() {return stack2.top();}};/*** Your MinStack object will be instantiated and called as such:* MinStack* obj = new MinStack();* obj->push(x);* obj->pop();* int param_3 = obj->top();* int param_4 = obj->min();*/
09.用两个栈实现队列
核心在于出现delete操作时就将两个栈倒装一次。保持队列的形式,只有当用于倒装的栈为空时,出现delete操作就会再次倒装。
class CQueue {
    stack<int> stack1,stack2;
public:
    CQueue() {
        while(!stack1.empty()){
            stack1.pop();
        } 
        while(!stack2.empty()){
            stack2.pop();
        }
    }//这个构造函数的作用就是清空两个栈
    void appendTail(int value) {
        stack1.push(value);
    }
    int deleteHead() {
        // 如果第二个栈为空
        if (stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.top());
                stack1.pop();
            }
        } 
        if (stack2.empty()) {
            return -1;
        } else {
            int deleteItem = stack2.top();
            stack2.pop();
            return deleteItem;
        }
    }
};
剑指 Offer II 036. 后缀表达式(逆波兰式)
意思就是把运算符放在后面
- 使用栈来实现
 - switch不能判断字符串
class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> stk; for(int i = 0; i < tokens.size(); i++){ string a = tokens[i]; if(isdigit(a[0])||isdigit(a[1])){ stk.push(stoi(tokens[i])); } else { int num1 = stk.top(); stk.pop(); int num2 = stk.top(); stk.pop(); switch (a[0]){ case '*': num1 = num1*num2;break; case '+': num1 = num1+num2;break; case '-': num1 = num2-num1;break; case '/': num1 = num2/num1;break; } stk.push(num1); } } return stk.top(); } }; 
剑指 Offer II 037. 小行星碰撞
就是情况有点多。
class Solution {
public:
    vector<int> asteroidCollision(vector<int>& asteroids) {
        stack<int> stk;
        for(int i = 0; i < asteroids.size(); i++){
            if(stk.empty()||asteroids[i]>0) {
                stk.push(asteroids[i]);
            } else {
                while(!stk.empty()&&stk.top() > 0&&abs(stk.top())<abs(asteroids[i])){
                    stk.pop();
                }
                if(stk.empty()||stk.top()<0) {
                    stk.push(asteroids[i]);
                } else if(abs(stk.top()) == abs(asteroids[i])) stk.pop();
            }
        }
        vector<int> res;
        while(!stk.empty()) {
            res.push_back(stk.top());
            stk.pop();
        }
        reverse(res.begin(), res.end());
        return res;
    }
};
单调栈
剑指 Offer II 038. 每日温度(单调栈)
可以维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        stack<int> stk;
        vector<int> res(n);
        for(int i = 0; i < n; i++){
            while(!stk.empty()){
                int pre = stk.top();
                if(temperatures[pre]>=temperatures[i]){
                    break;
                }
                res[pre] = i - pre;
                stk.pop();
            }
            stk.push(i);
        }
        return res;
    }
};
剑指 Offer II 039. 直方图最大矩形面积(hard)
- 注意单调栈存储的是下标
 - 每个栈顶下标都代表这当前的高度
 - 每当一个元素高度小于当前栈顶就代表找到了栈顶高度的右测,而因为栈是从小到大单调的,因此左侧也比较容易找到,就是当前栈顶的前一个元素(也就是当前栈顶pop之后的新的栈顶)。
 - 遇到比栈顶元素小的元素,不是算以当前元素为顶的矩形面积,而是算以栈顶元素为顶的矩形面积。有点反直觉。
 归纳一下枚举高的方法:
- 首先我们枚举某一根柱子i作为高h
 - 随后我们需要向两边扩展,使得扩展的柱子高度均不小于h。换句话说我们需要找到找到左右两侧最近的高度小于h的柱子。
 但是如果有两根柱子0和1,其中0在1的左侧,并且0的高度大于等于1,那么在后面的柱子i向左找小于其高度的柱子时,1会挡住0,0就不会作为答案了。
class Solution { public: int largestRectangleArea(vector<int>& heights) { stack<int> st;//单调栈是逐渐增大的,后入栈的应该都比前面的高度大。 //千万注意,单调栈存储的是下标 st.push(-1);//设置左墙壁 int n =heights.size(); int ans =0; for(int i=0;i<n;i++){ while(st.top()!=-1 && heights[st.top()]>=heights[i]){//循环维持栈的单调性 //计算过以栈顶元素的高之后,就pop出来 int tem = st.top(); st.pop(); //同11. 盛最多水的容器 一样高度减少 但是宽度增加 还是要比较面积大小 int curArea = heights[tem]*(i-st.top()-1);//当-1作为左端时,宽度实际上就是右端的下标 ans = max(ans,curArea); } st.push(i); } //此时的栈顶高度,索引后面的都比他大,索引前面的在栈中都比他小,因此右端为n,左端为新的栈顶。 while(st.top()!=-1){ int tem = st.top(); st.pop(); int curArea = heights[tem]*(n-st.top()-1); ans=max(ans,curArea); } return ans; } };剑指 Offer II 040. 矩阵中最大的矩形(重点)
这题就是上一题的加强版本
需要提前算出的左端最长连续个数
- 用这个i条件来作为单调栈建立的标准
 - heights表示当前行的每个位置的从上到下连续为1的次数
 后面的内容和上题类似,知道了每一列的连续1的次数,就相当于前面知道了高度。然后求建筑的最大面积。这里注意,行是会一直变化的,也就是说这一题的矩阵是可以不靠着x轴的,对应上一题也就是说建筑是可以悬空的。因此需要枚举每一行
class Solution { public: int maximalRectangle(vector<string>& matrix) { int n = matrix.size(); if(n == 0) return 0; int ans = 0; vector<int> heights(matrix[0].size()); //这里的这个可以通过类似dp的方法得到 //这里还进行了状态压缩 for(string row : matrix){//提前得到左端的最长连续1个数 for(int i = 0; i < row.size(); i++){ heights[i] = row[i] == '0' ? 0 : heights[i] + 1; } ans = max(ans, maximalRectangle(heights)); } return ans; } int maximalRectangle(vector<int>& heights){ int n = heights.size(); int ans = 0; stack<int> s; s.push(-1);// 防空 //后面的部分就和直方图最大矩形类似了 for(int i = 0; i < n; i++){//栈是从小到大,当遇到小于栈顶的情况时,就找到了以当前栈顶为宽度的情况。此时前后都小于它 //这一部分是每一列的情况 while(s.top() != -1 && heights[s.top()] >= heights[i]){ int tp = s.top(); s.pop(); ans = max(ans, heights[tp] * (i - s.top() - 1));//下端-上端-1 } s.push(i); } //栈中剩下来的元素都比后面的小,因此下端默认为n了 while(s.top() != -1){ int tp = s.top(); s.pop(); ans = max(ans, heights[tp] * (n - s.top() - 1)); } return ans; } };
