队列的出入条件 是先进先出,就是队头出,队尾入,根据这个特性,我们可以用两个指针来代表队头的位置与队尾的位置,队头为front,队尾为 rear。
我们假设一个长度为5的数组,来模拟队列:
这时 rear和front 指针都指向 下标0的位置,所以我们判断队列为空时,就是当 rear指针==front指针。然后让a1,a2,a3,a4 入队,如图:
所以当a1,a2,a3,a4入队时,front指针不动,让rear指针依次指向下一个下标,这时rear指针则指向下标4,这时我们在进行出队操作,让a1,a2出队。如图:
我们可以看到 a1,a2出队时,rear指针不动,front指针依次指向下一个下标,这时front指针指向 下标2;这时我们再插入a5,如图
这时我们的rear 已经指向了数组的外面,所以已经代表无法入队了,但我们可以看到队列中的个数并没有超过5个,但是以及无法进行入队操作,这种情况,我们通常叫做“假溢出”,所以,像这种顺序队列 十分容易照成“假溢出”的情况,这样空间利用率大大减小。
所以我们要怎么解决这种情况呢? 我们可以将rear 指针在数组外面的时候在指向头,就是头尾相连的循环。如图:
我们将rear 指针指向下标0,这样我们就不会出现指针指向不明,而且可以解决“假溢出”的情况。这是我们将 a6,a7入队,如图:
这时我们的rear指针指向下标 2,这个时候我们可以看到我们的rear指针与front指针 都为2,这时相等。 所以队满的情况为 rear == front,但是我们判断队空的条件也是 rear = = front,所以我们无法区分当rear=front的时候,是队空还是队满。
所以我们将队空的条件保持不变为rear = front ,并且当队列满时,我们改变条件,当队列还剩一个空间时 判断为队满,就是当队满的时候,数组还剩一个单元。如图;
这种情况我们就判断为队满。
但是,我们可以看出有时 rear>front,有时front>rear,所以我们并不好计算队满的条件。然后队列的最大长度为 Max_size,即我们可以判断队满的条件可以为 (rear + 1) % Max_size == front ,这样不管rear是否比front大,都可以判断。
这时我们就可以开始模拟代码了:
#define Max_size 100
typedef struct
{
int *data;
int front;//头指针
int rear;//尾指针
}SqQueue;
1.初始化
int InitQueue(SqQueue& q)//初始化空队列
{
q.data = (int*)malloc(sizeof(int) * Max_size);//动态分配空间
if (!q.data)//如果分配失败,则停止程序
exit(0);
q.front = 0;
q.rear = 0;
return 1;
}//当 rear == front时,队列判断为空
2.入队
int EnQueue(SqQueue& q, int e)//插入,e为插入的值
{
if ((q.rear + 1) % Max_size == q.front)//当 rear 的下一个指标指向front的时候就代表队列满了
return 0;
q.data[q.rear] = e;//将e赋予队尾
q.rear = (q.rear + 1) % Max_size;//将rear指针后移一位。 如果是末尾的话则转到数组头部
return 1;
}
3.出队
int DeQueue(SqQueue& q, int* e)//删除队头元素,e为返回的值
{
if (q.rear == q.front)//判断队列是否为空
{
return 0;
}
*e = q.data[q.front];
q.front = (q.front + 1) % Max_size;//将front指针后移一位。 如果是末尾的话则转到数组头部
return 1;
}
4.销毁队列
int DestroyQueue(SqQueue& q)//销毁队列
{
if (q.data)//如果q.data不为空,则释放
free(q.data);
q.data = NULL;
q.rear = q.front = 0;
return 1;
}
5.求队列长度
int QUeueLength(SqQueue q)//求队列长度
{
return (q.rear - q.front + Max_size) % Max_size;
}