LeetCode题解之栈(一)

200 阅读3分钟

1.栈的基础知识

先进后出

2.基本题目

20. 有效的括号

class Solution {
public:
    bool isValid(string s) {
        //辅助栈,用map存对应关系,其实也可以不存,直接压入右括号
        stack<int> stk;
        unordered_map<char,char> p = {{'{','}'},{'[',']'},{'(',')'}};
        for(auto x:s)
        {
            if(x=='(' || x=='{' || x=='[') stk.push(x);
            else if(stk.size() && x == p[stk.top()]) stk.pop();
            else return false;
        }
        if(stk.empty()) return true;
        else return false;
    }
};

32. 最长有效括号

class Solution {
public:
class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int> stk;
        int res = 0;
        stk.push(-1);
        for(int i = 0; i < s.size(); i ++)
        {
            if(s[i] == '(') stk.push(i);
            else
            {
                stk.pop();
                if(stk.empty()) stk.push(i);//说明-1被弹出了,)是落单的
                else res = max(res, i - stk.top());
            }
        }
        return res;
    }
};

155. 最小栈(剑指 Offer 30. 包含min函数的栈)

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> stk;
    stack<int> min_stk;
    MinStack() {
        min_stk.push(INT_MAX);
    }  
    void push(int x) {
        stk.push(x);
        if(min_stk.top()>=x) min_stk.push(x);//注意相同也要入栈;相当于出现了新的最小值才入min_stk,且弹出时通过值的大小对比看是不是最小值被弹出了
    }    
    void pop() {
        int tmp = stk.top();
        stk.pop();
        if(min_stk.top()==tmp) min_stk.pop();
    }   
    int top() {
        return stk.top();
    }   
    int getMin() {
        return min_stk.top();
    }
};

剑指 Offer 09. 用两个栈实现队列

class CQueue {
public:
   vector<int> stk_1,stk_2;
   CQueue() {
       
   }    
   void appendTail(int value) {
       stk_1.push_back(value);
   }
   
   int deleteHead() {
       if(stk_2.empty())
       {
           while(!stk_1.empty())
           {
               stk_2.push_back(stk_1.back());
               stk_1.pop_back();
           }
       }
       if(!stk_2.empty())
       {
           int res = stk_2.back();
           stk_2.pop_back();
           return res;
       }
       else return -1;
   }
};

剑指 Offer 31. 栈的压入、弹出序列

class Solution {
public:
/*
用一个真栈模拟:遍历压入序列,先入栈,与弹出序列第一个元素相等则弹出,继续入栈,
*/
   bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
       //弹出序列第一个元素必须在stk中遍历到后立马弹出,否则之后不会有机会弹出
       //所以每push一个,就检查是否能pop,最后stk为空说明进出刚好
       //o(n)进出各一次
       if(pushed.empty() && popped.empty()) return true;
       if(pushed.empty() || popped.empty() || pushed.size() != popped.size()) return false;
       stack<int> s;
       int popid = 0;
       for(int pushid = 0; pushid < pushed.size(); pushid ++)
       {
           s.push(pushed[pushid]);
           while(!s.empty() && s.top() == popped[popid])//注意是while直接判断,一次性把压入和弹出的遍历掉
           {
               s.pop();
               popid++;  
           }
       }
       return s.empty();
   }
};

42. 接雨水

class Solution {
public:
    int trap(vector<int>& height) {
        //方法一:2重循环暴力:对每个[i]往左找max_left,往右找max_right,最后ans+= min(max_left,max_right)-height[i]
        //改进方法一:不用每次重新遍历,可以用o(n)的空间从左到右遍历一次存下i处的左边最大值
        if(height.empty())
            return 0;
        int ans = 0;
        int size = height.size();
        vector<int> left_max(size), right_max(size);
        left_max[0] = height[0];
        for (int i = 1; i < size; i++) {
            left_max[i] = max(height[i], left_max[i - 1]);
        }
        right_max[size - 1] = height[size - 1];
        for (int i = size - 2; i >= 0; i--) {
            right_max[i] = max(height[i], right_max[i + 1]);
        }
        for (int i = 1; i < size - 1; i++) {
            ans += min(left_max[i], right_max[i]) - height[i];
        }
        return ans;
}
};

递减的栈遇到大数可以开始计算,因为top的左右就确定了

class Solution {
public:
    //方法三:单调栈
    int trap(vector<int>& height)
    {
        int ans = 0;
        stack<int> st;
        for (int i = 0; i < height.size(); i++)
        {//栈不为空,且当前高度大于栈顶,说明可以计算栈顶的积水,一直循环:先弹出栈顶,找到l是下一个元素,r是cur
            while (!st.empty() && height[st.top()] < height[i])//遇到一个高的,会把前面所有矮的都算完,然后这个高的变成比top矮的后又入栈
            {
                int cur = st.top();
                st.pop();
                if (st.empty()) break;
                int l = st.top();
                int r = i;
                int h = min(height[r], height[l]) - height[cur];
                ans += (r - l - 1) * h;
            }
            st.push(i);//小于栈顶或者栈为空,把当前i加入栈
        }
        return ans;
    }
 };

739. 每日温度

class Solution {
public:
/*
单调栈,维护一个递减的数组,每次来了一个更大数的时候,记录被弹出的序号差值
没有被弹出的元素说明没有更大数了
*/
   vector<int> dailyTemperatures(vector<int>& T) {
       int n = T.size();
       vector<int> res(n, 0);
       stack<int> stk;
       for(int i = 0; i < n; i ++)
       {
           while(!stk.empty() && T[i] > T[stk.top()])
           {
               int tmp = stk.top();
               stk.pop();
               res[tmp] = i - tmp;
           }
           stk.push(i);
       }
       return res;
   }
};

84. 柱状图中最大的矩形

递增的栈遇到小数可以计算,因为top的左右就确定了

class Solution {
public:
   int largestRectangleArea(vector<int>& heights) {
      stack<int> stk;//存下标
      int res = 0;
      heights.push_back(0);//给heights最后加一个0,让最后剩的一个高度也有遇到的比它矮的值(0),然后这时top是-1,正好计算宽度
      int n = heights.size();
      stk.push(-1);//给栈底加一个-1
      for(int i = 0; i < n; i ++)//巧妙融合了2个while
      {
          //if(stk.size() == 1 || i == 0 || heights[i] >= heights[stk.top()]) {stk.push(i);continue;}//i==0是初始化,size==1是只有-1,也就是里面出完了,下一个直接加入
          while( stk.size()>1 && heights[stk.top()] > heights[i])//>1保证-1不会被使用弹出
          {
              int k = stk.top();
              stk.pop();
              res = max(res, heights[k] * (i - stk.top() - 1));
          }
          stk.push(i);
       }
   return res;
   }
};

85. 最大矩形

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
       stack<int> stk;//存下标
       int res = 0;
       heights.push_back(0);//给heights最后加一个0,让最后剩的一个高度也有遇到的比它矮的值(0),然后这时top是-1,正好计算宽度
       int n = heights.size();
       stk.push(-1);//给栈底加一个-1
       for(int i = 0; i < n; i ++)//巧妙融合了2个while
       {
           //if(stk.size() == 1 || i == 0 || heights[i] >= heights[stk.top()]) {stk.push(i);continue;}//i==0是初始化,size==1是只有-1,也就是里面出完了,下一个直接加入
           while( stk.size()>1 && heights[stk.top()] > heights[i])//>1保证-1不会被使用弹出
           {
               int k = stk.top();
               stk.pop();
               res = max(res, heights[k] * (i - stk.top() - 1));
           }
           stk.push(i);
        }
    return res;
    }
    int maximalRectangle(vector<vector<char>>& matrix) {
        int i,j;
        int m = matrix.size();
        if(m == 0) return 0;
        int n = matrix[0].size();
        vector<int> v(n);//v就是上一个函数的heights
        int k = 0;
        for(i = 0; i < m; i ++){
            for(j = 0; j < n; j ++)
              v[j] = matrix[i][j] == '1' ? v[j] + 1:  0;
            k = max(k, largestRectangleArea(v));//对每一行往上的[0,i-1]求heights,最后比较大小
        }
        
        return k;
    }
};

402. 移掉K位数字

class Solution {
public:
    string removeKdigits(string num, int k) {
//单调栈:维护递增序列,如果选择删掉一个数,肯定删掉末尾的数;遇到更小的就弹出且加入它,这样最大限度的让它去最高的位置
        vector<int> stk;
        int n = num.size();
        for(int i = 0; i < n; i ++)
        {
            while(stk.size() &&  stk.back() > num[i] && k) 
            {
                stk.pop_back();
                k --;
            }
            stk.push_back(num[i]);
        }
        while(k --)//递增序列末尾开始弹出即可
        {
            stk.pop_back();
        }
        string res;
        int j = 0;
        while(j < stk.size() && stk[j] == '0') j ++;//去除前导0
        for(; j < stk.size(); j ++) res.push_back(stk[j]);//加入答案
        if(!res.size()) return "0";//为空则返回0
        return res;
    }
};