队列的认识
队列?什么是队列?是一群人排成的长队吗?NoNoNo,计算机里的队列并不是你想象中的队列,它还是一种数据结构。就好比在上一篇博客里我讲到的栈一样,他们都是种数据结构。
下面我们来看一下百度百科对它的定义
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头(百度百科)。
如果你看不懂就对了,因为我们还没有学(此话只对没有学过队列的萌新说,大佬请回避)。今天,我们就来初步认识一下队列。
图文并茂学队列
如果说把栈比作成弹夹的话,那我们就可以把队列比作一排长队(顾名思义)怎么说呢,栈只有一个头,队列却又两个头,但是栈的那一个头既负责进,也负责出。而队列却不一样了,右边的头进,左边的头出。
当一个元素从队列的右边进的时候我们叫做进队,右边那个头就叫做队尾。此时刚刚进去的元素叫做队尾元素。当一个元素从队列左边出的时候我们把它称之为出队,出队的那个头叫做队尾,出队元素的后一个元素变成了这个队列新的队首元素。
如果还是有亿点蒙蒙的话咱们来看几张图吧:
下面这张图展示了队列:
接下来我们看看如何进队和出队:
别急,还有一个呢
队列的STL容器
上一次我们讲了栈的STL容器,这一次我们来讲讲队列的STL容器。
要想用STL容器,就少不了定义一个队列。
定义一个队列的格式是这样的:
queue<数据类型> q;
这仅仅是格式,来看看样例:
queue<int> q;
queue<char> q;
queue<double> q;
定义完了之后,我们就开始研究STL容器的使用。
和栈一样,队列同样可以判断队列是否为空:
q.empty(); //队列是否为空 空就是1 不是空就0
获取队列的长度:
q.size(); // 获取队列当前长度
入队与出队:
q.push(x);// x是入队元素
q.pop(); //出队
获取队首元素和获取队尾元素如下:
q.front(); //获取队首元素 可以输出
q.back(); //获取队尾元素 可以输出
获取当前队列的长度:
q.size(); //获取队列当前长度
其实队列STL容器中也有相对应的函数原型,除了q.push()是有返回值类型除外,其他函数都是void类型的。
例题
约瑟夫问题
题目描述
n n n 个人围成一圈,从第一个人开始报数,数到 m m m 的人出列,再由下一个人重新从 1 1 1 开始报数,数到 m m m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。书上表述是给出淘汰 n − 1 n-1 n−1 名小朋友,而该题是全部出圈。
输入格式
输入两个整数 n , m n,m n,m。
输出格式
输出一行 n n n 个整数,按顺序输出每个出圈人的编号。
样例 #1
样例输入 #1
10 3
样例输出 #1
3 6 9 2 7 1 8 5 10 4
提示
1 ≤ m , n ≤ 100 1 \le m, n \le 100 1≤m,n≤100
解题思路
本题是一种非常经典的而且有古老的问题。背景是这样的:
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
话不多说,我们开始讲述这道题的解题思路:
首先,要先输入有多少个小朋友以及数到哪一个数的倍数那个编号的的小朋友退出的数字;接着循环入队,注意:入队的是每一个小朋友的编号,而他的编号就是默认的按照从1……n的顺序来的
for(int i=1;i<=n;i++){
q.push(i);
}//循环入队
接着进入循环,如果说这位小朋友报道的数刚好是m的倍数是,这位小朋友就被get out了,所以在这里我们需要一个特判:
if(cnt%m==0){
q.pop();
}
q.pop()正是出队的函数。然后我们还要加一步,就是输出出局的那一位可怜的小朋友,输出什么呢?当然是输出队首元素啊:
if(cnt%m==0){
cout<<q.front()<<" ";
q.pop();
}
如果这一位小朋友非常幸运,报的不是m的倍数呢?因为我们这个游戏是一轮一轮来进行的,所以呢,这位幸运的小朋友要重新移到队尾去(嘻嘻,天网恢恢,疏而不漏,你们谁都别想逃出不可能出局这个人的法掌):
else{
q.push(q.front());
}
等一下,是不是忘了什么?当然,那位幸运的小朋友是不可能分身的,所以呢,还要出队呢!改后的代码:
else{
q.push(q.front());
q.pop();
}
然后,我们最后还漏掉了一位超级幸运的小朋友,因为他没有被淘汰出局,在代码的末尾把它输出就行了,接下来奉上完整代码:
#include<iostream>
#include<queue>
using namespace std;
queue<int> q;
int n,cnt=1,m;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
q.push(i);
}
while(q.size()!=1){
if(cnt%m==0){
cout<<q.front()<<" ";
q.pop();
}else{
q.push(q.front());
q.pop();
}
cnt++;
}
cout<<q.front();
return 0;
}
好了,今天的博文讲解就到这里,我们下篇博文再见,掰!!
彩蛋:下一节课将动态规划哟!!