因为最近学习了栈和队列,所以写篇博客以便今后复习。
1.栈
特点:栈(stack):是一种特殊的线性结构,它只能在一端 进行插入和删除操作。
只允许在栈顶进行插入和删除,所以栈的操作是按“后进先出”(Last In First Out)的原则进行的。
对于新手来说可能不好理解,假如有一个糖罐,宽度为1,开始没有糖,每颗糖宽度为1,你先塞进去的糖最后才会吃到,假入第一天塞了10颗,吃了3颗,吃的是最后放进去的三颗,数天后吃完了,最后吃完的是最先放进去的。
头文件:#include<stack>
声明栈:stack<类型> s;
基本操作:
s.push(v) 将元素v顶压入栈
s.pop() 删除栈顶元素
s.top() 返回栈顶元素的值
s.size() 返回栈中元素的个数
s.empty() 判断栈是否为空
注意:只有push函数才有参数, 栈空时候不能top和pop
例题:
我们做小学数学题时,数学式子里往往会加很多括号,有时候这些括号可能出错。比如下面式子: 32 x [ 56 + ( 72 – 65 ] x 25 ) 括号不匹配
32 x [ 56 + ( 72 – 65 x 25 ) ] 括号匹配
现在我们只关心括号是否匹配,所以,我们可以把式子中的括号单独提出来 [ ( ] ) [ ( ) ]
对于给出的由‘{’‘}’‘[’‘]’‘(’‘)’ 构成的长度不超过100000的括号序列, 请你快速回答里面的括号是否匹配
输入格式: 样例输入1:([{()[]}[()]()])
样例输出1: yes
样例输入2:([{([)]}[()]()])
样例输出2: no
怎么写???
如果暴力,一定会超时。
完整正解:
试问大家玩过消消乐没有?
如果我们发现匹配的右括号,就删掉它和左括号,如果最后栈为空,说明匹配,输出“yes”,反之不匹配。
文字代码如下:
头文件
设一个char类型的栈,以及一个字符串。
先输入,每次判断栈是否为空,若为否则判定栈顶与栈中括号是否匹配,匹配删掉栈顶,否则在栈顶压入一个括号,若最后栈为空,匹配,反之输出“no”。
代码:
#include <bits/stdc++.h>
using namespace std;
stack <int> a;
string s;
int p(char x, char y)
{
if(x=='(' && y==')') return 1;
if(x=='[' && y==']') return 1;
if(x=='{' && y=='}') return 1;
if(x=='<' && y=='>') return 1;
else return 0;
}
int main() {
getline(cin, s);
for(int i=0; i<s.length(); i++)
{
if(!a.empty() && p(a.top(), s[i])) a.pop();
else a.push(s[i]);
}
if(a.empty()) printf("yes");
else printf("no");
return 0;
}
好的可以讲下队列了~~~
2.队列
特点:它的特点是可以在一边进行删除操作,在另一遍进行插入操作。
允许删除的一端称为队首(front),允许插入的一端称为队尾(back)。
队列的修改是按先进先出(First In First Out)的原则进行
头文件:#include<queue>
声明队列:queue<类型> s;
基本操作:
q.push(x) 将元素x加入队列尾部
q.pop() 将队首元素删除
q.front() 返回队首元素的值
q.back() 返回队尾元素的值
q.size() 队列中元素的个数
q.empty() 判断队列是否为空
假如有一个电影,大家在排队买票,没有人插队,先到先看,后到后看
例题:
在新年舞会上,n名男士(编号1到n)和m名女士(编号1到m),各自排成一队。 跳舞开始时,依次从男队和女队的队头上各出一人配成舞伴。规定每个舞曲只能有一对跳舞者。一曲结束后,跳舞的一对舞者各自回到自己队伍的末尾。 舞会总共有k个舞曲,问每曲参与跳舞的男士和女士编号是多少?
输入:一行两队的人数和舞曲数量n,m,k
输出:k行,每行两个整数,表示对应舞曲的男士和女士的编号
输入样例: 3 4 6
输出样例:
1 1
2 2
3 3
1 4
2 1
3 2
此题过于简单所以直接上文字代码:
定义两个队列
输入
循环K次:先从两个队列队首取出两个人,输出编号后放到队尾
代码:
#include <bits/stdc++.h>
using namespace std;
queue<int> boy, girl;
int main() {
int n, m, k, i, b, g;
cin >> n >> m >> k;
for(i=1; i<=n; i++) boy.push(i);
for(i=1; i<=m; i++) girl.push(i);
for(i=1; i<=k; i++)
{
b=boy.front();
g=girl.front();
cout << b << " " << g << endl;
boy.pop();
girl.pop();
boy.push(b);
girl.push(g);
}
return 0;
}
3.双端队列
其实就是多了些操作
头文件:#include<deque>
声明:deque<类型> s;
s.push_front(x);
s.push_back(x)
s.pop_front();
s.pop_back();
s.top();
s.empty();
s.size();
多了些东西,速度慢了些。
好的重点来了:
4.单调队列
单调队列有一种单调性(保持单增或单减)
单增队列:新加进一个元素,要删除前面所有比它大的元素(重点:注意题目要求是否可以取等!!!)
单减相反
例题:
给你一个长度为N(N<=10^6)的数组,一个长为K的滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,找出窗体所包含的数字的最大和最小值,如下表所示:k的值为3
窗口位置 最小值 最大值 [1 3 -1] -3 5 3 6 7 -1 3 1 [3 -1 -3] 5 3 6 7 -3 3 1 3 [-1 -3 5] 3 6 7 -3 5 1 3 -1 [-3 5 3] 6 7 -3 5 1 3 -1 -3 [5 3 6] 7 3 6 1 3 -1 -3 5 [3 6 7] 3 7
输入格式
第1行为n,k,第2行为长度为n的数组。
输出格式
共2行,第1行是每个位置的min value,第2行是每个位置的max value。
样例输入
8 3
1 3 -1 -3 5 3 6 7
样例输出
-1 -3 -3 -3 3 3
3 3 5 5 6 7
想方法
暴力+优化可以。
单调队列
那单增还是单减?
单减!!!
Why?
Because
对区域最大值而言:如果出现递增,例如 x,y......(y>x)那么x比y先出队,只要有y在,x就不是最大值,x就可以删掉,形成一个单调递减的队列,而每一次取队首即可。
AC Code:
#include <bits/stdc++.h>
using namespace std;
int n, k, a[1000001];
deque<int> q;
int main() {
scanf("%d%d", &n, &k);
for(int i=1; i<=n; i++) scanf("%d", & a[i]);
for(int i=1; i<=k; i++)
{
while(!q.empty() && a[q.back()]>=a[i]) q.pop_back();
q.push_back(i);
}
printf("%d ", a[q.front()]);
for(int i=k+1; i<=n; i++)
{
while(!q.empty() && a[q.back()]>=a[i]) q.pop_back();
q.push_back(i);
if(q.front()<=i-k) q.pop_front();
printf("%d ", a[q.front()]);
}
printf("\n");
q.clear();
for(int i=1; i<=k; i++)
{
while(!q.empty() && a[q.back()]<=a[i]) q.pop_back();
q.push_back(i);
}
printf("%d ", a[q.front()]);
for(int i=k+1; i<=n; i++)
{
while(!q.empty() && a[q.back()]<=a[i]) q.pop_back();
q.push_back(i);
if(q.front()<=i-k) q.pop_front();
printf("%d ", a[q.front()]);
}
return 0;
}
悄悄附上优化了的暴力:
#include <iostream>
using namespace std;
const int N = 1000001;
int a[N], q[N];
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i ++) cin >> a[i];
int f = 0, r = -1;
for (int i = 0; i < n; i ++)
{
if (f <= r && i - q[f] + 1 > k) f ++ ;
while (f <= r && a[q[r]] >= a[i]) r -- ;
q[++r] = i;
if (i >= k - 1) cout << a[q[f]] << ' ';
}
cout << endl;
f = 0, r = -1;
for (int i = 0; i < n; i ++)
{
if (f <= r && i - q[f] + 1 > k) f ++ ;
while (f <= r && a[q[r]] <= a[i]) r -- ;
q[++r] = i;
if (i >= k - 1) cout << a[q[f]] << ' ';
}
return 0;
}
我的小秘密说一句:单调队列时间2549s,暴力1517ms
!!!???
快解释一下怎么回事!!!
重交一遍
单调队列时间957s,暴力1567ms
好的可以
算你挺机灵的
keke
总结完了,谢谢大家!