一.概述
因为队列的特殊构造,所以我们一般就像上节那样,使用链式列表。但是把,有些时候,顺序结构有顺序结构的好处,所以我们也是可以通过某种方式来用顺序表实现列表。那就是通过循环队列来实现。
二.原理实现
1.节点
因为是顺序结构,所以我们节点不需要其他的东西,只需要储存数据就可以了。
2.循环队列结构
因为循环队列的特殊机制,我们一般不考虑对队列进行扩容。那么就因该有一个固定的长度MaxSize
。一个队列因该包含三个部分基址*base
、front头指针
、tail尾指针
。因为是循环的,所以我们的尾指针的值,可能会在头指针的前面。比如MaxSize=6 front=4 tail=2
时,我们的队列长度因该是4 5 0 1
这4个位置,计算的话,因该是(tail-front+MaxSize)%Maxsize
。
3.队列的初始化以及空队列判断
队列初始化,直接动态生成*base
的储存空间,然后再将front=tail=0
。如果是空队列,那么因该front=tail
。
4.队列队满的判断
我们队列满的时候,有两种情况,头在尾前:tail=MaxSize-1
、 front=0
;还有一种功能情况就是尾在头前:tail+1=front
。那么,总结一下,它的判断因该是(tail+1)%MaxSize=front
。
5.队列的入队
入队的话,我们只需要把tail
的地方写入我们插入的值即可,然后再++tail
但是可能会出现tail+1=MaxSize
的情况,所以我们的需要用一个if
或者计算
来修改它的值。个人推荐使用计算tail = (tail+1)%MaxSize
。不过理论上来说,用if来更新也不会出错。但是有时候,我们可能会用到快慢指针
的方式做一些题,所以用计算的这种方式的容错率可能会好很多。
但注意,如果我们先判断队列是否满的话会导致队列最大的存储元素个数是Maxsize-1
,因为tail
始终是尾元
的位置,它是直接可用的,而判满是tail+1=front
,所以导致了最后一个元素没法用。除非我们修改下判断队满的方式,就如下图J8实际上是上因该是没有数据的:
6.队列的出队
出队和入队相似,但是顺序结构我们不需要对地址做什么,只需要把头指针front+1
就可以了。当然,也还有超过MaxSize
的情况,所以我们还是得采用一些容错机制:front = (front+1)%MaxSize
三.代码实现
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
#define OverFlow 2
#define MaxSize 5
typedef struct dataType{
int data;
}dataType;
typedef struct cqueue{
dataType *base;
int front;
int tail;
}cqueue;
cqueue* CqueueInit(void);
int CqueuePush(cqueue* q, dataType data);
int CqueueGet(cqueue *q, dataType *data);
int CqueuePop(cqueue *q);
int CqueueShow(cqueue *q);
int main()
{
// 主函数随便改
cqueue *q = CqueueInit();
dataType test;
test.data = 0;
CqueuePush(q,test);
test.data = 1;
CqueuePush(q,test);
test.data = 2;
CqueuePush(q,test);
test.data = 3;
CqueuePush(q,test);
CqueuePop(q);
test.data = 4;
CqueuePush(q,test);
CqueueShow(q);
return 0;
}
cqueue* CqueueInit(void)
{
cqueue *q = (cqueue*)malloc(sizeof(cqueue));
dataType *data = (dataType*)malloc(sizeof(dataType)*MaxSize);
if (q != NULL && data != NULL)
{
q->base = data;
q->front = 0;
q->tail = 0; // 初始化循环队列
return q;
}
exit(0);
}
int CqueuePush(cqueue* q, dataType data)
{
if ((q->tail+1)%MaxSize == q->front) return OverFlow;
// 注意这样的写法会导致最后有一个节点存不到数据
*(q->base+q->tail) = data;
q->tail += 1;
return OK;
}
int CqueueGet(cqueue *q, dataType *data)
{
if ((q->tail - q->front)%MaxSize == 0)
{
return ERROR;
}
else
{
*data = *(q->base+q->tail); // 返回头指针指向的值
return OK;
}
}
int CqueuePop(cqueue *q)
{
q->front += 1; // 更新一下指向的元素
return OK;
}
int CqueueShow(cqueue *q) // 这个函数就打印一下队列 看看效果
{
int start,end = 0;
int cont = 0;
if (q->tail < q->front) // 队尾在队头的前面
{
end = q->tail + MaxSize;
}
else
{
end = q->tail; // 队头在队尾的前面
}
for(start = q->front; start < end; ++start)
{
printf("No.%d data is %d\n", cont, (q->base+start)->data);
++cont;
}
}