队列(queue)与优先队列(priority_queue)

队列

在数据结构中也称为操作受限的线性表,是一种只允许在表的一端插入,在另一端删除的线性表。

相关的几个概念

1)队尾rear:插入端,线性表的表尾;

2)队首front:删除端,线性表的表头;

3)空队列:当队列中没有任何元素时,称为空队列。

特点

先进先出(FIFO,first in first out),就像在食堂排队买饭,排在最前面的先买,后来的排在队尾,即删除在队首,插入在队尾

queue容器的使用

定义

queue<typedef> name;//typedef可以是任意基本数据类型或容器。

如:queue<int> q;//整型队列容器

queue队列操作

(1) push(x); 将x入队,即将x插入到队尾。

(2)front(x); 获得队首元素。

(3)back(x); 获得队尾元素(这个我感觉不咋用)。

(4)pop(); 弹出队首元素。

(5)empty(); 检测队列是否为空。

(6)size(); 返回队列内元素个数。

例题

林大oj 1632 周末舞会

#include <bits/stdc++.h>//万能头文件
using namespace std;
queue<int>vis1,vis2;//定义两个队列
int main()
{
    int m,n,k;//男士人数m,女士人数n,舞曲数目k
    while(cin>>m>>n)
    {
        cin>>k;
        int v1,v2;
        for(int i=1;i<=m;i++)
          vis1.push(i);//压入队列
        for(int i=1;i<=n;i++)
          vis2.push(i);
        while(k--)
        {
            v1=vis1.front();//获取队首元素
            v2=vis2.front();
            vis1.pop();//队首元素出列
            vis2.pop();
            cout<<v1<<" "<<v2<<endl;
            vis1.push(v1);
            vis2.push(v2);
        }
    }
    return 0;
}

林大oj 1634 约瑟夫环

#include <bits/stdc++.h>
using namespace std;
queue<int>s;
int main()
{
    int n,m;
    cin>>n>>m;
        int k=0,t;
        for(int i=1;i<=n;i++)
            s.push(i);//压入队列
        while(s.size()>1)//只要还有一个及以上的人在,就继续
        {
            k++;
            t=s.front();//取出队首元素
            s.pop();//将队首元素出列
            if(k%m!=0)//检查
                s.push(t);//不是要找的,先放回队列
        }
        cout<<s.front()<<endl;//仅剩下最后一个,打出
    return 0;
}

林大oj 1635 酒桌游戏

#include <bits/stdc++.h>//problem 1635
using namespace std;
queue<int> vis;
map<int,string> mp;//将人名与编码联系起来
map<int,string>::iterator it;//迭代器
int fun(int n)
{
    while(n)
    {
        if(n%10==7)
            return 1;
        n/=10;
    }
    return 0;
}
int main()
{
    int n,m,t,temp;
    string name,ans;
    while(cin>>n>>m>>t)
    {
        mp.clear();
        for(int i=1;i<=n;i++)
            vis.push(i);//将编号压入队列
        for(int i=1;i<=n;i++)
        {
            cin>>name;
            mp[i]=name;//输入人名并将其与编号一一对应
        }
        for(int i=1;i<m;i++)
        {
            vis.push(vis.front());//将第m号人的前面几个人顺次插入队尾
            vis.pop();
        }
        while(vis.size()>1)
        {
            if(fun(t)==1||t%7==0)//检查
            {
                vis.pop();
            }
            else
            {
                vis.push(vis.front());
                vis.pop();
            }
            t++;
        }
        cout<<mp[vis.front()]<<endl;//输出对应编码的人名
    }
    return 0;
}

这个代码用到了map(学会了这个之后我本人非常喜欢用hhh,后面也会出一个关于map使用的文章),有的同学可能还没学到,下面是不使用map的代码。

#include <bits/stdc++.h>
using namespace std;
struct node
{
    int num;
    string name;
}p[1001];//这里不是用map,而是用结构体来记录编号和名字
queue<node>q;//队列用于模拟传递过程
bool judge(int x)
{
    if(x%7==0)
        return 1;
    while(x)
    {
        if(x%10==7)
        return 1;
        x=x/10;
    }
    return 0;
}
int main()
{
    int n,m,t;
    string name;
    cin>>n>>m>>t;
    for(int i=1;i<=n;i++)
    {
        cin>>p[i].name;
        p[i].num=i;
    }
    for(int i=1;i<=n;i++)
        q.push(p[i]);
    for(int i=1;i<=m-1;i++)
    {
        q.push(p[i]);
        q.pop();
    }
    while(q.size()>1)
    {
        node tmp=q.front();
        q.pop();
        if(!judge(t))
            q.push(tmp);
            t++;
    }
/*在这个循环中,每次从队列中取出队首的人物信息 tmp,
然后检查 t 是否满足 judge(t) 的条件。如果不满足条件,
则将 tmp 放回队列尾部,并将 t 自增。
直到队列中只剩下一个人满足条件时,循环结束。*/
    printf("%s\n",q.front().name.c_str());
    return 0;
}

关于参数 q.front().name.c_str()

  • q.front() 是队列 q 中的第一个元素,类型是 node 结构体。
  • name 是 node 结构体中的一个成员变量,类型是 string,用来存储参与者的名字。
  • .c_str() 是 string 类的成员函数,用于返回一个指向以空字符结尾的C风格字符串的指针(即 const char* 类型)。
  • printf("%s\n", q.front().name.c_str()); 的作用是将队列 q 中第一个人物的名字以C风格的字符串形式输出到标准输出(通常是控制台)。
  • %s 接收 q.front().name.c_str() 返回的字符串指针,并将其输出。

林大oj 1633 取牌游戏

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+1;
queue<int>vis;//创建队列模拟牌堆
int a[N];
int main()
{
    int n,k,p,num=0,q=0;
/*n表示玩家数,k表示牌数,p表示每次移动的牌数,
num用于计数,q用于记录“good”牌的位置*/
    cin>>n>>k>>p;
    for(int i=1;i<=k;i++)
    vis.push(i);
    while(!vis.empty())
    {
        int tmp=vis.front();
        vis.pop();
        num++;
        if(num%n==0)//从队列首部取出一张牌,若该牌是"good",将其位置记录在数组a中。
            a[q++]=tmp;
        if(!vis.empty())
            for(int i=1;i<=p;i++)//将p张牌一张张顺次移动到最后,放在牌堆底部
        {
            int tp=vis.front();
            vis.pop();
            vis.push(tp);
        }
    }
    sort(a,a+k/n);//对数组a进行排序,以便输出时是按照升序的
    for(int i=0;i<k/n;i++)//输出数组a中的所有元素,即"good"牌的位置
        cout<<a[i]<<endl;
    return 0;
}
/*由于题目中k是n的倍数,所以“good”牌的数量m就是k除以n的结果*/

优先队列

优先队列(Priority Queue)是一种特殊的队列数据结构,它与普通队列的主要区别在于每个元素都关联有一个优先级。在优先队列中,元素按照优先级的高低进行排列,而不是严格按照它们被插入的顺序。

定义

priority_queue<typename> name;

//typename可以是任意基本数据类型或容器,按照降序对队列元素排序。

如:priority_queue<int> pq;//整型降序优先队列

注:优先级队列默认使用vector作为其底层存储数据的容器, 在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_ queue就是,所有需要用到堆的位置,都可以考虑使用priority_ queue 。

常见操作

(1)push(x); 将x插入到优先队列中。加入元素后会自动调整priority_ queue的内部结构以保证队首元素(堆顶元素)的优先级最高。

(2)top(); 获取队首元素,即优先级最高元素(优先队列没有front和back函数哟~)。

(3)pop(); 令队首元素出队,出队后会调整堆内部结构。

(4)empty(); 检测优先队列内部是否为空。

(5)size(); 返回优先队列内的元素个数。

优先队列元素优先级的设置

定义优先队列内元素的优先级是运用好优先队列的关键,以基本数据类型和结构体类型为例:

基本数据类型的优先级设置:


优先队列默认是数字大的优先级越高,以int为例,以下两个语句等价:

priority_ queue<int> pq;
priority_ queue <int, vector<int>less<int> > pq;

// 注意> >之间的空格。

参数vector<int>填写的是来承载底层数据结构堆的容器
less <int>是对第一个参数int的比较类,less <int>表示数字大的优先级越大,而greater<int>表示数字小的优先级越大。想让优先队列总是把最小的的元素放在队首,则使用语句:

priority_ queue <int, vector <int>greater<int> > pq;

例题 林大oj 1688 合并果子

例如有 3 种果子,数目依次为 1 , 2 , 9 。可以先将 1 、 2 堆合并,新堆数目为 3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力 =3+12=15 。可以证明 15 为最小的体力耗费值。

#include <bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> >v;//这句是从小到大排序,也可以用结构体排序
int main()
{
    ios::sync_with_stdio(0);
    int n,a,b,sum=0,x;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>x;
        v.push(x);
    }
    while(v.size()>=2)
    {
       a=v.top();v.pop();
       b=v.top();v.pop();
       v.push(a+b);
       sum+=a+b;
    }
    cout<<sum<<endl;
    return 0;
}

结构体的优先级设置

重载结构体变量"<”"和“>”关系运算符,实现结构体变量的比较
less调用”<"运算符,greater 调用">” 运算符

注:如果题目中只需要利用小数优先级高的情形,可以把”<"运算符的实现修改为”>”的比较方法,这样就不需要用greater了,只用默认的less就可以了。但是,容易引起歧义。

这里介绍一下operator < 重载的写法:
主要决定着你的变量或者结构体在优先队列中是升序还是降序排列的!

#include <bits/stdc++.h>
using namespace std;
struct sa{
int x;
};
bool operator < (const sa &a,const sa &b)//这里的< 一直不变
{
    return a.x>b.x;  //这里的> <决定是升序还是降序,>号升序,< 降序
}
priority_queue <sa> vis;
int main()
{
for(int i=1;i<=10;i++)
vis.push({i});
sa tmp;
while(!vis.empty())
{
    tmp=vis.top();
    cout<<tmp.x<<endl;
     vis.pop();
}
return 0;
}

例题 林大oj 1537 买饭

有n个人在一个卖饭窗口前排队买饭,假如每个人买饭的时间为t,请编程找出一种这n个人排队的顺序,使得这n个人的平均等待时间最小。

#include <bits/stdc++.h>
using namespace std;
struct sa
{
    int time;
    int id;
 };
bool operator < (const sa &a,const sa &b)
{
    if (a.time!=b.time)
    return a.time>b.time;
    else
        return a.id>b.id;
}
priority_queue<sa>vis;

int main()
{
   int n,x;
   double sum=0.0;
   cin>>n;
   for(int i=1;i<=n;i++)
   {
       cin>>x;
       vis.push({x,i});//x是time,i是id,结构体用花括号
   }
   struct sa tmp;
   for(int i=1;i<=n;i++)
   {
     tmp=vis.top();
     if (i!=n)
     cout<<tmp.id<<" ";
     else
        cout<<tmp.id<<endl;
     sum+=(n-i)*tmp.time;//n-i表示当前人后面还有几个人,tmp.time表示当前人的买饭时间
     vis.pop();
   }
   printf("%.2lf\n",sum/n);
    return 0;
}

考试太多,很久没有更新了,这一篇也是断断续续的写了很久hhh(草稿堆里还有一大堆半成品2333),感觉后半段写的挺潦草的。。。如有问题,欢迎指出交流,祝各位天天开心~

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值