栈和队列复习

本文介绍了栈和队列的基本概念、操作及在编程中的应用,特别是单调队列的原理和使用,通过实际例子展示了如何利用单调队列解决特定问题,如括号匹配和舞会配对问题。
摘要由CSDN通过智能技术生成

 因为最近学习了栈和队列,所以写篇博客以便今后复习。

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

总结完了,谢谢大家!

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值