本以为循环队列是个高级操作,后来发现是个必备操作,不然太浪费地儿了
解释如下
还是先介绍下队列的顺序存储结构
typedef struct
{
int *base;
int front;
int rear;
}sqqueue;
来,和栈的顺序存储结构比较一下
typedef struct
{
int *base;
int *top;
int stacksize;
}
栈的栈底是不动的,所以可用base来指向栈的首地址,top上下移动,就像一个注射器一样.
队列首尾都要变,所以要另设一个base来记录队列地址,另设两个指针front,rear(感觉都不能算准确的指针,就是记住首尾的数组下标)
接下来解释下为什么是循环队列,上图
控制面板真心不好写字…
如图,队列放入了6个元素,然后又删除了3个元素,那如果让你继续放元素呢,放不下了!可是还有一半的空间没用呢,为了避免这种极度浪费,人们想出了循环队列. 逻辑上,让队列的首尾连接起来.
即上图再放如a7时,就让a7放到a1的位置,这有点像数学里面的周期函数,f(1)=f(T+1),这里的T就等于6,其实就是队列的最大容量.
但是这样的话,你无法判断队列满了没有,因为尾指针总在最后一个元素的上一位,这样的话,队列满时的条件Q.rear==Q.front和空队时是一模一样的.
解决办法之一就是少用一个空间,当队列还有一个空间没用时就判断队列已满,即 (Q.rear+1)%maxsize==Q.front (Q.front也要进行%maxsize的处理,只不过是在后面) 这样,就和空队列的判断不一样了.
高中我们经常写周期函数的题,自变量X除以周期T,余几,就是f几.
循环队列是一个道理,它们又有点像两个小孩在田径场的圆圈跑道上跑步,同一起跑线出发,后出发的小孩一直追着先出发的小孩跑,永远无法超过.当先出发的小孩比后出发的小孩多跑了快一圈时,游戏就结束了,即队列满了.
下面上队列的基本操作:
1.循环队列的初始化
2.求循环队列的长度
3.循环队列的入队
4.循环队列的出队
5.取循环队列的队头元素
开始开始
1.循环队列的初始化
#define overflow -2
#define maxsize 100
int initqueue(sqqueue &Q)
{
Q.base=new int[maxsize]; //为队列分配一个最大容量为maxsize的空间
if(!Q.base)exit(overflow); //存储分配失败
Q.front=Q.rear=0; //头指针和尾指针置为0,队列为空
}
2.求队列长度
int queuelength(sqqueue Q)
{
return (Q.rear+maxsize-Q.front)%maxsize;
}
括号里面加maxsize是为了避免Q.rear-Q.front为负数
3.循环队列的入队
int enqueue(sqqueue &Q,int e)
{
if((Q.rear+1)%maxsize==Q.front) //队满,无法再放入
return error;
Q.base[Q.rear]=e; //新元素插入队尾
Q.rear=(Q.rear+1)%maxsize; //队尾指针加一
}
4.循环队列的出队
int dequeue(sqqueue &Q,int &e)
{
if(Q.rear==Q.front) //队空,没法出
return error;
e=Q.base[Q.front]; //保存队头元素
Q.front=(Q.front+1)%maxsize; //队头指针加一
}
5.取队头元素
int gethead(sqqueue Q)
{//返回Q的队头元素,不修改队头指针
if(Q.rear!=Q.front)
return Q.base.[Q.front];
}