• 【常用数据结构——队列&&栈】(最喜欢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     

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

     
      
  • 相关阅读:
    计算机病毒
    wordpress搬家教程
    javascript的DOM学习之选项卡制作
    javascript的DOM学习上
    [转]jQuery 引用地址{包括jquery和google提供的地址}, 节省你不必要的流量
    CSS3属性之:transition
    CSS3属性之:animastion
    css3学习系列之box-shadow(1)
    使ie678支持css3伪类选择器的插件
    个人网站名称填写注意事项
  • 原文地址:https://www.cnblogs.com/hualian/p/11341192.html
Copyright © 2020-2023  润新知