单调栈
单调栈:栈中数据从栈底至栈顶具有单调性。要求遍历的序列中每个元素都必须要进入一次单调栈。由于其单调性,不要求每个元素在遍历序列之后仍然保留在单调栈内。
应用:求解 N G E / N L E 、 P G E / P L E NGE/NLE、PGE/PLE NGE/NLE、PGE/PLE问题( N e x t / P r e v i o u s G r e a t e r / L e s s E l e m e n t Next/Previous\ Greater/Less\ Element Next/Previous Greater/Less Element)。
-
G
r
e
a
t
e
r
Greater
Greater类问题:构造单调递减栈
L e s s Less Less类问题:构造单调递增栈 -
P
r
e
v
i
o
u
s
Previous
Previous类问题:以即将入栈的元素为视角看栈顶元素,不允许栈顶元素与即将入栈元素相同(入栈前必须将栈中与其相同元素全部岀栈)
N e x t Next Next类问题:以栈顶元素为视角看即将入栈元素,允许即将入栈元素与栈顶元素相同
N G E / N L E NGE/NLE NGE/NLE问题
-
N G E NGE NGE:构造单调递减栈,以栈顶元素为视角看即将入栈元素,允许即将入栈元素与栈顶元素相同
-
N L E NLE NLE:构造单调递增栈,以栈顶元素为视角看即将入栈元素,允许即将入栈元素与栈顶元素相同
//tuple三个数据分别为:data:元素本身 index:元素所在下标 ans:答案所在下标
extern vector<tuple<int,int,int>>v;
extern stack<tuple<int,int,int>>s;
void nge(){
for(int i=0;i<v.size();i++){
if(s.empty()||get<0>(s.top())>=get<0>(v[i])){//nle为<= 允许栈顶元素和即将入栈元素相同
s.push(v[i]);
}else{
while(s.size()&&get<0>(s.top())<get<0>(v[i])){//nle为>
get<2>(v[get<1>(s.top())])=get<1>(v[i]);
s.pop();
}
s.push(v[i]);
}
}
}
P G E / P L E PGE/PLE PGE/PLE问题
- P G E PGE PGE:构造单调递减栈,以入栈元素为视角看栈顶元素,不允许栈顶元素与即将入栈元素相同(入栈前必须将栈中与其相同元素全部岀栈)
- P L E PLE PLE:构造单调递增栈,以入栈元素为视角看栈顶元素,不允许栈顶元素与即将入栈元素相同(入栈前必须将栈中与其相同元素全部岀栈)
//tuple三个数据分别为:data:元素本身 index:元素所在下标 ans:答案所在下标
extern vector<tuple<int,int,int>>v;
extern stack<tuple<int,int,int>>s;
void pge(){
for(int i=0;i<v.size();i++){
if(s.empty()){
s.push(v[i]);
}else if(get<0>(v[i])<=get<0>(s.top())){//ple为>=
while(get<0>(v[i])==get<0>(s.top())) s.pop();//不允许栈顶元素与即将入栈元素相同
get<2>(v[i])=get<1>(s.top());
s.push(v[i]);
}else{
while(!s.empty()&&get<0>(v[i])>get<0>(s.top())) s.pop();//ple为<
get<2>(v[i])=get<1>(s.top());
s.push(v[i]);
}
}
}
单调队列
单调队列:队列中数据从队头到队尾具有单调性。要求遍历的序列中每个元素都必须要进入一次单调队列。由于其单调性,不要求每个元素在遍历序列之后仍然保留在单调队列内。
由于需要对队列进行队尾入队,队头、队尾出队,故采用双端队列(deque)实现。
应用:滑动窗口最值问题、最大子序和问题
滑动窗口最值问题
滑动窗口利用双指针的思想,是尺取法的应用。用单调队列可解决滑动窗口的最值问题。
- 最大值问题:维护单调递减队列
- 最小值问题:维护单调递增队列
关键操作:
- 删头:若队头元素脱离窗口(超过窗口范围),将队头元素从队头出队
- 去尾:若新元素在从队尾入队时,原队尾破坏了队列的单调性,使原队尾出队(因为必须保证每个元素都能入队,由于滑动窗口的滑动特性,不要求每个元素都持续在队列中)
extern int n,k;//n:序列长 k:窗口长
//data index
extern vector<pair<int,int>>a;
extern deque<pair<int,int>>m;
void solve(){
//单增队列
for(int i=0;i<n;i++){
while(!m.empty()&&m.back()>a[i]) m.pop_back();//<为单减队列
m.push_back(a[i]);
if(i>=k-1){//窗口大小已取足
while(!m.empty()&&get<1>(m.back())-get<1>(m.front())>=k) m.pop_front();
cout<<get<0>(m.front())<<' ';
}
}
}
最大子序和问题
(未完待续)