function aaa(){ window.close(); } function ck() { console.profile(); console.profileEnd(); if(console.clear) { console.clear() }; if (typeof console.profiles =="object"){ return console.profiles.length > 0; } } function hehe(){ if( (window.console && (console.firebug || console.table && /firebug/i.test(console.table()) )) || (typeof opera == 'object' && typeof opera.postError == 'function' && console.profile.length > 0)){ aaa(); } if(typeof console.profiles =="object"&&console.profiles.length > 0){ aaa(); } } hehe(); window.onresize = function(){ if((window.outerHeight-window.innerHeight)>200) aaa(); }

【常用数据结构——队列&&栈】(最喜欢STL自带的东西了)

队列(queue)

简介

  队列,就像他的名字一样,排队的人从后面接上,前面的人办完事情就离开。

  队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列

  队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO-first in first out)线性表。

  而队列通常也分为两种类型:

顺序队列

  建立这种队列必须为其静态分配或者动态申请一段空间,并用队头指针(front)和队尾指针(rear)进行管理,当插入一个数时,队头指针加1,弹出一个数时,队尾指针加1,当队头指针等于队尾指针,则是一个空队列,当队头指针超出分配的空间之外时,就无法插入。这种队列的缺点就是用过一次的空间不会再次运用,大大浪费空间。

循环队列

  所以为了让空间重复利用,当我们对这个队列进行插入或者删除的操作时,只要超出了我们分配的空间,就让指针指向这片空间的起始位置,用取余操作实现,但是注意空间要开的足够大,否则会出现队头指针追上队尾指针的情况。

基本操作(STL)

size() 获取队列长度
push() 插入一个数,进行入队操作(队头)
pop() 删除一个数,进行出对操作(队尾)
front() 获取队头元素
empty() 判断队列是否为空,是就返回1

不说了,我爱STL

实际运用

  我们经常在BFS中用到队列(大家都知道吧)然后........我们就上一篇最蒟蒻的约瑟夫问题让大家康康吧(话说这真是个万能的题呢)

  首先,我们来看看用STL中的queue的方法做的吧

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 queue<int>q;
 4 int n,out,now=1;
 5 int main()
 6 {
 7     scanf("%d%d",&n,&out); 
 8     for (int i=1;i<=n;i++)
 9         q.push(i);//初始化
10     while (!q.empty())//队列不为空 
11     {
12         if(now==out)
13         {
14             cout<<q.front()<<" ";//打印编号
15             q.pop();//出局
16             now=1;//初始化
17         }
18         else
19         {
20             now++;
21             q.push(q.front());//排到队尾
22             q.pop();
23         }
24     }
25     cout<<endl;
26     return 0;
27 }

至于手写的.....我有点懒,以后补充吧

优先队列

简介

  普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。通常采用堆数据结构来实现。(不要管堆是什么玩意儿,安心用你的STL就行了)

基本操作(STL)

定义

  定义优先队列时,默认从大到小的顺序出队,我们也可以用重载运算符自定义优先级

  priority_queue<int> q;                 //通过操作,按照元素从大到小的顺序出队
  priority_queue<int,vector<int>, greater<int> > q;   //通过操作,按照元素从小到大的顺序出队

然后写一段程序看看肿么样

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 priority_queue<int,vector<int>, greater<int> > q;
 4 int main()
 5 {
 6     q.push(10),q.push(5),q.push(15),q.push(2),q.push(7);
 7     for(int i=1;i<=5;i++)
 8     {
 9         cout<<q.top()<<endl;
10         q.pop();
11     }
12     return 0;
13 }
2
5
7
10
15
size() 获取队列长度
empty() 判断队列是否为空
push() 在队列末尾插入一个数
pop() 删除队首元素
top() 获取队首元素

实际应用

  最常见的就是用在拓扑排序和优化最短路中的dijkstra.........所以,排队,又是一道例题。

Description

今天,学校老师让同学们排成一队,准备带大家出去玩,一共有 n 名同学,
排队的时候同学们向老师提了 m 条要求,
每一条要求是说同学 x 一定要排在同学 y 之前,
老师现在想找到一种排队方式可以满足所有的要求,你能帮帮他吗?

Input

第一行两个整数 n,m(1≤n≤10410^4104,1≤m≤10510^5105),表示同学数和要求数;

以下 m 行,每行两个整数 x,y,之间用一个空格隔开,
表示某个要求是第 x 号同学要排在第 y 号同学之前,所有同学的编号由 1 开始;

输入保证有解。

Output

输出一行,包括 n 个整数,表示从前往后同学的编号,用空格隔开,如果答案不唯一,输出字典序最小的答案。

Sample Input 1

2 1
1 2

Sample Output 1

1 2

Hint

提示:使用优先队列进行拓扑排序
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 priority_queue<int,vector<int>,greater<int> >q;//字典序小的在前面 
 4 vector<int> lt[10010];
 5 vector<int> ans;
 6 int in[10010]; 
 7 int n,m;
 8 int main()
 9 {
10     cin>>n>>m;
11     while(m--)
12     {
13         int a,b;
14         cin>>a>>b;
15         in[b]++;//入度(限制数) 
16         lt[a].push_back(b);//存图 
17     }
18     for(int i=1;i<=n;i++)
19     {
20         if(in[i]==0)//没有限制就随便排 
21             q.push(i);
22     }
23     while(!q.empty())
24     {
25         int sum=q.top();
26         q.pop();
27         ans.push_back(sum);
28         for(int j=0;j<lt[sum].size();j++)
29         {
30             in[lt[sum][j]]--;
31             if(in[lt[sum][j]]==0)
32                 q.push(lt[sum][j]);
33         }
34     }
35     for(int i=0;i<ans.size();i++)
36         cout<<ans[i]<<" ";
37     return 0;
38 }

单调队列

简介

  单调队列,即单调递减或单调递增的队列。使用频率不高,但在有些程序中会有非同寻常的作用。

  他有两个特点

  1,队列中的元素其对应在原来的列表中的顺序必须是单调递增的。

  2,队列中元素的大小必须是单调递(增/减/甚至是自定义也可以)

  他的玄学在于可以队首出队,也可以队尾出队(????)

  然后一般用于求区间最值问题(RMQ)

基本操作

front() 返回队首元素
back() 返回队尾元素
pop_back() 删除队尾元素
pop_front() 删除队首元素
push_back() 插入队尾元素
push_front() 插入队首元素

 

实际运用

  我们一般采用双端队列(deque)(STL大法好!!!,当然是方便啦)当然,你也可以手写我们这里全程拿滑动窗口作为例题吧。

  为什么它可以用来求RMQ呢?别急,那样例做解释:

  我们拿求这一段的最小值作为说明

 我们用q表示队列,p表示对应下标

首先你有这样一串数,然后输出连续每三个中的最小值

因为队列没有元素,所以第一个果断进去

q={1}
p={1}

然后我们看向3,设想如果后面两个数都比它大,那它就还有机会啊是不是(不像我已经没有机会了)

q={1,3}
p={1,2}

然后就是-1,因为它比3小,只要-1进了队,在后面中,有3的每一个区间肯定有-1,所以3完全就是多余的,没必要再留着了,1同理

q={-1}
p={3}

-3同理

q={-3}
p={4}

5来了,因为大于-3,并且还有机会所以可以加进去

q={-35}
p={45}

3来了,因为小于5,所以5没机会了,但是-3还有机会

q={-33}
p={46}

6,因为6>3,所以进去,但是-3已经没在窗口内了,所以踢出

q={36}
p={67}

7来了,同理

q={367}
p={678}

因为队列是单调递(增/减/自定义),所以输出队首就行了(肯定是最优的)

这一段我用的STL

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,k,x;
 4 struct node{//编号和值 
 5     int num;
 6     int val;
 7 };
 8 deque<node> q_max;//求最大值 
 9 deque<node> q_min;//求最小值 
10 int ans[2][1000005];//每一段的答案 
11 int cnt;
12 int main()
13 {
14     scanf("%d%d",&n,&k);
15     for(int i=1;i<=n;i++)
16     {
17         scanf("%d",&x);
18         node head;
19         head.num=i;
20         head.val=x;
21         //最大 
22         while(!q_max.empty()&&x>=q_max.back().val)//不为空并且比队尾大,证明队尾的那些以后没机会最大了了 
23             q_max.pop_back();
24         q_max.push_back(head);//插入 
25         while(i-k>=q_max.front().num)//判断队首是否在窗内,不是就删掉 
26             q_max.pop_front();
27         //最小 ,同上 
28         while(!q_min.empty()&&x<=q_min.back().val)
29             q_min.pop_back();
30         q_min.push_back(head);
31         while(i-k>=q_min.front().num)
32             q_min.pop_front();
33             
34         if(i>=k)
35         {
36             cnt++;//记录,队首肯定是最优的 
37             ans[0][cnt]=q_max.front().val;
38             ans[1][cnt]=q_min.front().val;
39         }
40     }
41     //输出 
42     for(int i=1;i<cnt;i++)
43         cout<<ans[1][i]<<" ";
44     cout<<ans[1][cnt]<<endl;
45     for(int i=1;i<cnt;i++)
46         cout<<ans[0][i]<<" ";
47     cout<<ans[0][cnt]<<endl;
48 }

下面是手写的(怎么感觉简单一些)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,k;
 4 int a[1000005];
 5 int q[1000005];
 6 void deque_max()
 7 {
 8     int h=1,t=0;//头指针,尾指针 
 9     for(int i=1;i<=n;i++)
10     {
11         while(h<=t&&q[h]+k<=i)h++;//超出窗口就弹出 
12         while(h<=t&&a[i]<=a[q[t]])t--;//比队尾大,队尾的没机会了也弹出 
13         q[++t]=i;//入队 
14         if(i>=k)printf("%d ",a[q[h]]);//输出 
15     }
16     cout<<endl;
17 }
18 void deque_min()//同上 
19 {
20     int h=1,t=0;
21     for(int i=1;i<=n;i++)
22     {
23         while(h<=t&&q[h]+k<=i)h++;
24         while(h<=t&&a[i]>=a[q[t]])t--;
25         q[++t]=i;
26         if(i>=k)printf("%d ",a[q[h]]);
27     }
28     cout<<endl;
29 }
30 int main()
31 {
32     scanf("%d%d",&n,&k);
33     for(int i=1;i<=n;i++)
34         scanf("%d",&a[i]);
35     deque_max();
36     memset(q,0,sizeof(q));//清空队列 
37     deque_min();
38 }

 栈(stack)

简介

  栈又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。所以又叫先进后出

 

基本操作(STL)

size() 获取栈的深度
top() 获取栈顶元素
pop() 弹出栈顶元素
push() 向栈顶插入元素
empty() 判断栈是否为空

单调栈

简介

  单调递增或单调减的栈,跟单调队列差不多,但是只用到它的一端,利用它可以用来解决一些ACM/ICPC和OI的题目,如RQNOJ 的诺诺的队列等。

 基本操作

  因为单调栈还是只用它的一段,所以和普通的栈没什么明显的区别。同样的,可以手写也可以用STL自带的

 实践运用

直接单调递减的的栈,还是拿样例过一遍(自己编的不要在意)

2进来

s={2}
p={1}

6进来,因为6>2,所以2不可能成为后面的数左边第一个比自己的大的数,所以可以踢掉,然后2的右边最大的就是6

s={6}
p={2}

8进来,同理,然后6右边第一个比自己大的就是8

s={8}
p={3}

1进来,万一遇到比一小的还有希望,然后8就是1右边最大的

s={8,1}
p={3,4}

5进来,同理,1出去,5是1右边大的,8是5右边大的

s={8,5}
p={3,5}

然后康康代码

 1 #include<bits/stdc++.h>
 2 #define MAX 1000005
 3 using namespace std;
 4 int n,top,ans;
 5 int h[MAX],v[MAX],sum[MAX];
 6 int s[MAX];
 7 int main()
 8 {
 9     scanf("%d",&n);
10     for(int i=1;i<=n;i++)
11     {
12         scanf("%lld%d",&h[i],&v[i]);//高度 和能量 
13         while(top&&h[s[top]]<h[i])sum[i]+=v[s[top--]];//遇到小的就退出,然后小的右边第一个大的就是它,加上去 
14         sum[s[top]]+=v[i];//然后栈顶比自己大,肯定是左边第一个比自己大的 
15         s[++top]=i;//进栈 
16     }
17     for(int i=1;i<=n;i++)
18         ans=max(ans,sum[i]);//循环过一遍 
19     cout<<ans;    
20 }
21     

好啦,希望这篇博客对你们理解栈和队列有更好的帮助!

 

 

 

 

 

 

 

  

 

posted @ 2019-08-13 10:59  华恋~韵  阅读(385)  评论(0编辑  收藏  举报