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;
}
};