假设有这样一道题目,桌面上有一堆牌(n),从上往下依次记录为1~n,每次从牌顶取一次放出来,然后从剩下的牌的牌顶中取一张牌拿出来放在牌的末尾,问放出去的牌的顺序是?
这个题目显然就是用队列的思想来解决的一个问题,首先依次从队列中取出2个元素,把第一个元素从队列中放出去,第二个元素放在队列的末尾,依次这样循环下去,直到队列中没有元素为止,所以首先想到的代码如下:
#include<stdio.h>
#define max 100;
int queue[max];
int main(void){
int num,front,rear;
scanf("%d",&num);
for(int i=0;i<num;i++){
queue[i] = i + 1; //向队列中添加元素
}
front = 0; //front指向第一个元素
rear = n; //rear指向最后一个元素的下一个元素
while(front < rear){
printf("%d",queue[front++]); //输出第一个元素,并且让当前的front+1
queue[rear++] = queue[front++]; //把当前的队列元素放在队尾,并且front和rear的值都+1
}
return 0;
}
如果把这段代码拿进去做测试的话,可以看到我们希望得到的结果,但是程序有一定的问题,那就是当输入的值比较大的时候,程序可能访问非法内存。很显然的是最后的rear不会等于n,如果输入的数字比较大的时候,我们必须开辟大量的内存才能满足程序的合法性,那么有没办法,让空间被重复利用呢?解下来就是循环队列上场了
#include <stdio.h>
#include <string.h>
#define max 100
typedef struct NODE{
int data[max];
int rear;
int front;
}queue,*QUEUE;
void init(queue *q){
q->rear=0; //rear指向第一个元素,front指向最后一个元素的下一个元素,初始化的时候让他们都设置为0
q->front = 0;
}
int isEmpty(queue *q){
//当rear和front相等的时候,说明队列已经为空了
if(q->rear == q->front){
return 1;
}
return 0;
}
int isFull(queue *q){
//rear+1的值对数组的最大元素取余与front的值相等的时候,说明队列已经满了
if((q->rear+1)%max == q->front){
return 1;
}
return 0;
}
void inQueue(queue *q,int num){
if(isFull(q)){
return;
}else{
//入队后,我们让当前的rear值走一步,因为是循环队列,所以完全有可能最后rear从数组的末尾走到数组的开始,所以对(rear+1)%max;
q->data[q->rear]= num;
q->rear = (q->rear+1)%max;
}
}
void outQueue(queue *q,int *number){
if(isEmpty(q)){
return;
}else{
*number = q->data[q->front];
//出队是让当前面的front进一,这里的意思给入队时候的rear差不多
q->front = (q->front+1)%max;
}
}
int main(void){
int i,n;
scanf("%d",&n);
int num1,num2;
queue q;
init(&q); //初始化队列
for(i=0;i<n;i++){
inQueue(&q,i+1); //向队列中添加元素
}
while(q.front!=q.rear){ //当front不等于rear的时候,说明队列中还有元素
outQueue(&q,&num1); //出队,并且记录当前的值
printf("%d",num1); //打印出当前的值
if(isEmpty(&q)){ //注意:因为我们用循环队列的话,这里很有可能出现队列中只有一个元素的情况,这时上面的代码已经把这个元素给出队了,所以我们这里要判断如果没有队列的时候就把这个程序给终止了
break;
}
outQueue(&q,&num2); //当前元素出队
inQueue(&q,num2); //把上面出队的元素放在队尾
}
return 0;
}
这段代码比上面的好处就是队列中的空间可以反复利用,不需要浪费更多的内存空间。循环队列的一个主要思想就是用一个front指向队列的首元素,rear指向队尾元素的下一个元素,然后不停的这样周期下去。