数据结构--循环队列
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构--循环队列
⼀.顺序队列的改进
队列元素的出列是在队头,即下标为0的位置,那也就意味着,中的所有元素都得向前移动,以保证队列的队头(也就是下标为0的位置)不为空,此时的时间复杂度为0(n)。
可有时想想,为什么出队列时⼀定要全部移动呢,如果不去限制队列的元素必须存储在数组的前n个单元这⼀条件,出队的性能就会⼤⼤增加。
也就是说,队头不需要⼀定在下标为0的位置,⽐如也可以是a[1]等。
⽽为了避免当只有⼀个元素时,队头和队尾重合使处理变得⿇烦,引⼊两个指针,front指针指向队头元素,rear指针指向队尾元素的下⼀个位置,这样当front等于rear时,此队列不是还剩⼀个元素,⽽是空队列。
对于队列最好的⽅法是使⽤链表实现,因为对于数组来说,队列可能会出现下⾯这种情况:
假设是长度为5的数组,初始状态,空队列如所⽰,front与 rear指针均指向下标为0的位置。
然后⼊队a1、a2、a3、a4, front指针依然指向下标为0位置,⽽rear指针指向下标为4的位置。
出队a1、a2,则front指针指向下标为2的位置,rear不变,如下图所⽰,再⼊队a5,此时front指针不变,rear指针移动到数组之外。
嗯?数组之外,那将是哪⾥?
问题还不⽌于此。
假设这个队列的总个数不超过5个,但⽬前如果接着⼊队的话,因数组末尾元素已经占⽤,再向后加,就会产⽣数组越界的错误,可实际上,我们的队列在下标为0和1的地⽅还是空闲的。
我们把这种现象叫做“”。
不可以继续添加元素,否则会造成数组越界⽽遭致程序出错。
然⽽此时⼜不应该扩充数组,因为还有⼤量实际空间未被占⽤。
此时我们应该如何解决这个问题呢?我们将其实现为循环队列。
解决假溢出的办法就是后⾯满了,就再从头开始,也就是头尾相接的循环。
我们把队列的这种头尾相接的顺序存储结构称为。
也就是利⽤循环来解决空间浪费问题。
⼆.循环队列的引⼊
2.1总结来说,什么是循环队列?
由于在⽤数组实现队列的时候,队列有元素出列,front就向后移动,所以队列前⾯的空间就空了出来。
当rear移动到LENGTH时,再⼊队会发⽣假溢出,也就是说实际上我们开辟的数组还有剩余空间,却因为rear越界表现为溢出,
为了更合理的利⽤空间,⼈们想了⼀个办法:将队列的⾸尾相连接。
这样当rear移动到LENGTH时,会再从0开始循环。
那当什么时候队列满呢?当rear等于front的时候。
可是队列为空的时候也是同样的条件,那不就没法判断了吗?
办法⼀是设置⼀个标志变量flag,当front == rear,且flag = 0时为队列空,当front == rear,且flag= 1时为队列满
办法⼆牺牲⼀个存储空间,front前⾯不存数据,当rear在front前⾯的时候就是满了(尾在头前就是满了)
我们主要讨论第⼆种⽅法=
2.2理解循环队列
当队列空时,条件就是from = rear,当队列满时,我们修改其条件,保留⼀个元素空间。
也就是说,队列满时,数组中还有⼀个空闲单元。
如下图所⽰,我们就认为此队列已经满了,
由于rear可能⽐front⼤,也可能⽐front⼩,所以尽管它们只相差⼀个位置时就是满的情况,但也可能是相差整整⼀圈。
所以若队列的最⼤尺⼨为QueueSize,那么队列满的条件是(rear+1) %QueueSize == front (取模“%的⽬的就是为了整合rear与front⼤⼩为⼀个问题)。
⽐如上⾯这个例⼦, QueueSize = 5,当 front=0,⽽ rear=4, (4+1) %5 = 0,所以此时队列满。
再⽐如,front = 2⽽rear =1。
(1 + 1) %5 = 2,所以此时队列也是满的。
⽽对于下图, front = 2⽽rear= 0, (0+1) %5 = 1,1!=2,所以此时队列并没有满。
另外,当rear > front时,此时队列的长度为rear—front。
但当rear < front时,队列长度分为两段,⼀段是QueueSize-front,另⼀段是0 + rear,加在⼀起,队列长度为rear-front + QueueSize,
因此通⽤的计算队列长度公式为:
(rear—front + QueueSize) % QueueSize
总结
队空条件:front == rear
队满条件:(rear+1) %QueueSize == front
队列长度:(rear—front + QueueSize) % QueueSize
三.循环队列的代码
3.1.循环队列各个参数的含义
(1)队列初始化时,front和rear值都为零;
(2)当队列不为空时,front指向队列的第⼀个元素,rear指向队列最后⼀个元素的下⼀个位置;
(3)当队列为空时,front与rear的值相等,但不⼀定为零;
3.2循环队列⼊队的伪算法
(1)把值存在rear所在的位置;
(2)rear=(rear+1)%maxsize ,其中maxsize代表数组的长度;
bool Enqueue(PQUEUE Q, int val)
{
if(FullQueue(Q))
return false;
else
{
Q->pBase[Q->rear]=val;
Q->rear=(Q->rear+1)%Q->maxsize;
return true;
}
}
3.3.循环队列出队的伪算法
(1)先保存出队的值;
(2)front=(front+1)%maxsize ,其中maxsize代表数组的长度;
bool Dequeue(PQUEUE Q, int *val)
{
if(EmptyQueue(Q))
{
return false;
}
else
{
*val=Q->pBase[Q->front];
Q->front=(Q->front+1)%Q->maxsize;
return true;
}
}
3.4.如何判断循环队列是否为空
if(front==rear)
队列空;
else
队列不空;
bool EmptyQueue(PQUEUE Q)
{
if(Q->front==Q->rear) //判断是否为空
return true;
else
return false;
}
3.5.如何判断循环队列是否为满
解决这个问题有两个办法:⼀是增加⼀个参数,⽤来记录数组中当前元素的个数;第⼆个办法是,少⽤⼀个存储空间,也就是数组的最后⼀个存数空间不⽤,当(rear+1)%maxsiz=front时,队列满;
bool FullQueue(PQUEUE Q)
{
if(Q->front==(Q->rear+1)%Q->maxsize) //判断循环链表是否满,留⼀个预留空间不⽤
return true;
else
return false;
}
完整代码
queue.h⽂件代码:
#ifndef __QUEUE_H_
#define __QUEUE_H_
typedef struct queue
{
int *pBase;
int front; //指向队列第⼀个元素
int rear; //指向队列最后⼀个元素的下⼀个元素
int maxsize; //循环队列的最⼤存储空间
}QUEUE,*PQUEUE;
void CreateQueue(PQUEUE Q,int maxsize);
void TraverseQueue(PQUEUE Q);
bool FullQueue(PQUEUE Q);
bool EmptyQueue(PQUEUE Q);
bool Enqueue(PQUEUE Q, int val);
bool Dequeue(PQUEUE Q, int *val);
#endif
queue.c⽂件代码:
#include<stdio.h>
#include<stdlib.h>
#include"malloc.h"
#include"queue.h"
/***********************************************
Function: Create a empty stack;
************************************************/
void CreateQueue(PQUEUE Q,int maxsize)
{
Q->pBase=(int *)malloc(sizeof(int)*maxsize);
if(NULL==Q->pBase)
{
printf("Memory allocation failure");
exit(-1); //退出程序
}
Q->front=0; //初始化参数
Q->rear=0;
Q->maxsize=maxsize;
}
/***********************************************
Function: Print the stack element;
************************************************/
void TraverseQueue(PQUEUE Q)
{
int i=Q->front;
printf("队中的元素是:\n");
while(i%Q->maxsize!=Q->rear)
{
printf("%d ",Q->pBase[i]);
i++;
}
printf("\n");
}
bool FullQueue(PQUEUE Q)
{
if(Q->front==(Q->rear+1)%Q->maxsize) //判断循环链表是否满,留⼀个预留空间不⽤
return true;
else
return false;
}
bool EmptyQueue(PQUEUE Q)
{
if(Q->front==Q->rear) //判断是否为空 return true;
else
return false;
}
bool Enqueue(PQUEUE Q, int val)
{
if(FullQueue(Q))
return false;
else
{
Q->pBase[Q->rear]=val;
Q->rear=(Q->rear+1)%Q->maxsize; return true;
}
}
bool Dequeue(PQUEUE Q, int *val)
{
if(EmptyQueue(Q))
{
return false;
}
else
{
*val=Q->pBase[Q->front];
Q->front=(Q->front+1)%Q->maxsize; return true;
}
}
参考资料:。