数据结构-顺序队列的实现
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 循环队列定义
所以解决"假溢出"的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。
刚才的例子继续,上图的rear可以改为指向下标为0的位置,这样就不会造成指针指向不明的问题了,如下图所示。
接着入队a6,将它放置于下标为0处,rear指针指向下标为1处,如下图左所示。若再入队a7,则rear指针就与front指针重合,同时指向下标为2的位置,如下图右所示。
- 此时问题又出来了,我们刚才说,空队列时,front等于rear,现在当队列满
时,也是front等于rear,那么如何判断此时的队列究竟是空还是满呢?- 办法一是设置一个标志变量flag, 当front == rear,且flag = 0时为队列空,
当front== rear,且flag= 1 时为队列满。- 办法二是当队列空时,条件就是front = rear,当队列满时,我们修改其条件,保留一个元素空间。也就是说,队列满时,数组中还有一个空闲单元。
例如下图所示,我们就认为此队列已经满了,也就是说,我们不允许上图右的情况出现。
我们重点来讨论第二种方法,由于rear可能比front大,也可能比front小,所以尽管它们只相差一个位置时就是满的情况,但也可能是相差整整一圈。 所以若队列的最大尺寸为QueueSize,那么队列满的条件是(rear+1) % QueueSize == front
。
通用的计算队列长度公式为:(rear - front + QueueSize) % QueueSize
。
注意:front指针和rear指针后移不能直接使用++,而要使用%:Q->front = (Q->front + 1) % MAXSIZE
,因为到达数组尾后需要移动到数组开头。
3 循环队列完整实现
#include "stdafx.h"
#include "stdlib.h"
#define TRUE 1
#define FALSE 0
#define MAXSIZE 5 /* 存储空间初始分配量 */
typedef int Status;
typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */
/* 循环队列的顺序存储结构 */
typedef struct
{
QElemType data[MAXSIZE];
int front; /* 头指针 */
int rear; /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}SqQueue;
Status visit(QElemType c)
{
printf("%d ", c);
return TRUE;
}
/* 初始化一个空队列Q */
Status InitQueue(SqQueue *Q)
{
Q->front = 0;
Q->rear = 0;
return TRUE;
}
/* 将Q清为空队列 */
Status ClearQueue(SqQueue *Q)
{
Q->front = Q->rear = 0;
return TRUE;
}
/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
Status QueueEmpty(SqQueue Q)
{
if (Q.front == Q.rear) /* 队列空的标志 */
return TRUE;
else
return FALSE;
}
/* 返回Q的元素个数,也就是队列的当前长度 */
int QueueLength(SqQueue Q)
{
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
/* 若队列不空,则用e返回Q的队头元素,并返回TRUE,否则返回FALSE */
Status GetHead(SqQueue Q, QElemType *e)
{
if (Q.front == Q.rear) /* 队列空 */
return FALSE;
*e = Q.data[Q.front];
return TRUE;
}
/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(SqQueue *Q, QElemType e)
{
if ((Q->rear + 1) % MAXSIZE == Q->front) /* 队列满的判断 */
return FALSE;
Q->data[Q->rear] = e; /* 将元素e赋值给队尾 */
Q->rear = (Q->rear + 1) % MAXSIZE;/* rear指针向后移一位置, */
/* 若到最后则转到数组头部 */
return TRUE;
}
/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(SqQueue *Q, QElemType *e)
{
if (Q->front == Q->rear) /* 队列空的判断 */
return FALSE;
*e = Q->data[Q->front]; /* 将队头元素赋值给e */
Q->front = (Q->front + 1) % MAXSIZE; /* front指针向后移一位置, */
/* 若到最后则转到数组头部 */
return TRUE;
}
/* 从队头到队尾依次对队列Q中每个元素输出 */
Status QueueTraverse(SqQueue Q)
{
int i;
i = Q.front;
while ((i + Q.front) != Q.rear)
{
visit(Q.data[i]);
i = (i + 1) % MAXSIZE;
}
printf("\n");
return TRUE;
}
int main()
{
Status j;
int i = 0, l;
QElemType d;
SqQueue Q;
InitQueue(&Q);
printf("初始化队列后,队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));
printf("请输入整型队列元素(不超过%d个),-1为提前结束符: ", MAXSIZE - 1);
do
{
/* scanf("%d",&d); */
d = i + 100;
if (d == -1)
break;
i++;
EnQueue(&Q, d);
} while (i<MAXSIZE - 1);
printf("队列长度为: %d\n", QueueLength(Q));
printf("现在队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));
printf("连续%d次由队头删除元素,队尾插入元素:\n", MAXSIZE);
for (l = 1; l <= MAXSIZE; l++)
{
DeQueue(&Q, &d);
printf("删除的元素是%d,插入的元素:%d \n", d, l + 1000);
/* scanf("%d",&d); */
d = l + 1000;
EnQueue(&Q, d);
}
l = QueueLength(Q);
printf("现在队列中的元素为: \n");
QueueTraverse(Q);
printf("共向队尾插入了%d个元素\n", i + MAXSIZE);
if (l - 2>0)
printf("现在由队头删除%d个元素:\n", l - 2);
while (QueueLength(Q)>2)
{
DeQueue(&Q, &d);
printf("删除的元素值为%d\n", d);
}
j = GetHead(Q, &d);
if (j)
printf("现在队头元素为: %d\n", d);
ClearQueue(&Q);
printf("清空队列后, 队列空否?%u(1:空 0:否)\n", QueueEmpty(Q));
return 0;
}
/*
输出结果:
初始化队列后,队列空否?1(1:空 0:否)
请输入整型队列元素(不超过4个),-1为提前结束符: 队列长度为: 4
现在队列空否?0(1:空 0:否)
连续5次由队头删除元素,队尾插入元素:
删除的元素是100,插入的元素:1001
删除的元素是101,插入的元素:1002
删除的元素是102,插入的元素:1003
删除的元素是103,插入的元素:1004
删除的元素是1001,插入的元素:1005
现在队列中的元素为:
1002 1003 1004 1005
共向队尾插入了9个元素
现在由队头删除2个元素:
删除的元素值为1002
删除的元素值为1003
现在队头元素为: 1004
清空队列后, 队列空否?1(1:空 0:否)
*/
从这一段讲解,大家应该发现,单是顺序存储,若不是循环队列,算法的时间性能是不高的,但循环队列又面临着数组可能会溢出的问题。
原文地址:https://www.cnblogs.com/linuxAndMcu/p/10327803.html