一.
队头元素总在[0]单元,不必设头指针,但出队元素时要移动大量元素,耗时长,效率低
#define QUEUE_INIT_SIZE 10//队列存储空间的初始分配量
#define QUEUE_INCREMENT 2//队列存储空间的分配增量
typedef int QElemType;
struct SqQueue1
{
QElemType *base;//初始化的动态分配存储空间
int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置
int queuesize;//当前分配的存储容量(以sizeof(QElemType)为单位)
};
void InitQueue(SqQueue1 &Q)
{
//构造空队列
Q.base = (QElemType*)malloc(QUEUE_INIT_SIZE*sizeof(QElemType));
if (!Q.base)
{
exit(ERROR);//存储分配失败
}
Q.rear = 0;//空队列,尾指针为0
Q.queuesize = QUEUE_INIT_SIZE;//初始存储容量
}
void DestroyQueue(SqQueue1 &Q)
{
//销毁队列
free(Q.base);//释放存储空间
Q.base = NULL;
Q.rear = Q.queuesize = 0;
}
void ClearQueue(SqQueue1 &Q)
{
//清空队列
Q.rear = 0;
}
Status QueueEmpty(SqQueue1 Q)
{
//判断队列是否为空
if (Q.rear == 0)
return TRUE;
else
return FALSE;
}
int QueueLength(SqQueue1 Q)
{
//返回Q的元素个数,即队列的长度
return Q.rear;
}
Status GetHead(SqQueue1 Q, QElemType &e)
{
//返回队头元素
if (Q.rear)
{
e = *Q.base;
return OK;
}
else
{
return ERROR;
}
}
void EnQueue(SqQueue1 &Q, QElemType e)
{
//插入新元素到队尾
if (Q.rear == Q.queuesize)//当前存储空间已满
{
//增加分配
Q.base = (QElemType*)realloc(Q.base, (Q.queuesize + QUEUE_INCREMENT)*sizeof(QElemType));
if (!Q.base)
{
//分配失败
exit(ERROR);
}
Q.queuesize += QUEUE_INCREMENT;//增加存储容量
}
Q.base[Q.rear++] = e;//入队新元素,队尾指针+1
}
Status DeQueue(SqQueue1 &Q, QElemType &e)
{
//删除队头
int i;
if (Q.rear)//队列不空
{
e = *Q.base;
for (i = 1; i < Q.rear; i++)//依次前移队列元素
{
Q.base[i - 1] = Q.base[i];
}
Q.rear--;
return OK;
}
else
{
return ERROR;
}
}
void QueueTraverse(SqQueue1 Q, void(*vi)(QElemType))
{
//从队头到队尾依次对队列Q中每个元素调用函数vi()
int i;
for (i = 0; i < Q.rear; i++)
{
vi(Q.base[i]);
}
printf("\n");
}
void print1(QElemType i)
{
printf("%d", i);
}
void main()
{
Status j;
int i, k = 5;
QElemType d;
SqQueue1 Q;
InitQueue(Q);
printf("初始化队列后,队列是否是空队列?%u(1:空 2:否)\n", QueueEmpty(Q));
for (i = 1; i <= k; i++)
EnQueue(Q, i);//依次入队k个元素
printf("依次入队%d个元素后,队列中的元素为", k);
QueueTraverse(Q, print1);
printf("队列长度为%d,队列空否?%u(1:空 2:否)\n", QueueLength(Q), QueueEmpty(Q));
DeQueue(Q, d);
printf("出队一个元素,其值为%d\n", d);
j = GetHead(Q, d);
if (j)
printf("现在队头元素是%d\n", d);
ClearQueue(Q);
printf("清空队列后,队列空否?%d(1:空 2:否)\n", QueueEmpty(Q));
DestroyQueue(Q);
}
运行结果:
二.
另一种顺序存储结构,在出队元素时不移动元素,只改变头指针的位置
struct SqQueue2
{
QElemType *base;//初始化的动态分配存储空间
int front;//头指针,若队列不空,指向队列头元素
int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置
int queuesize;//当前分配的存储容量(以sizeof(QElemType)为单位)
};
//顺序队列的基本操作
void InitQueue(SqQueue2 &Q)
{
Q.base = (QElemType*)malloc(QUEUE_INIT_SIZE*sizeof(QElemType));
if (!Q.base)
{
exit(ERROR);
}
Q.front = Q.rear = 0;
Q.queuesize = QUEUE_INIT_SIZE;
}
void DestroyQueue(SqQueue2 &Q)
{
if (Q.base)
free(Q.base);
Q.base = NULL;
Q.front = Q.rear = Q.queuesize = 0;
}
void ClearQueue(SqQueue2 Q)
{
Q.front = Q.rear = 0;
}
Status QueueEmpty(SqQueue2 Q)
{
if (Q.front == Q.rear)
return TRUE;
else
return FALSE;
}
Status GetHead(SqQueue2 Q, QElemType &e)
{
if (Q.front == Q.rear)
return ERROR;
e = Q.base[Q.front];
return OK;
}
//顺序非循环队列的基本操作
int QueueLength(SqQueue2 Q)
{
return (Q.rear - Q.front);
}
void EnQueue(SqQueue2 &Q, QElemType e)
{
if (Q.rear == Q.queuesize)
{
//队满,增加存储单元
Q.base = (QElemType*)realloc(Q.base, (Q.queuesize + QUEUE_INCREMENT)*sizeof(QElemType));
if (!Q.base)//增加单元失败
exit(ERROR);
}
Q.base[Q.rear++] = e;
}
Status DeQueue(SqQueue2 &Q, QElemType &e)
{
if (Q.front == Q.rear)
return ERROR;
e = Q.base[Q.front++];
return OK;
}
void QueueTraverse(SqQueue2 Q, void(*vi)(QElemType))
{
int i = Q.front;
while (i != Q.rear)
{
vi(Q.base[i++]);
}
printf("\n");
}
void print2(QElemType i)
{
printf("%d", i);
}
void main()
{
Status j;
int i, n = 11;
QElemType d;
SqQueue2 Q;
InitQueue(Q);
printf("初始化队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
printf("队列的长度为%d\n", QueueLength(Q));
printf("请输入%d个整形队列元素:\n", n);
for (i = 0; i < n; i++)
{
scanf("%d", &d);
EnQueue(Q, d);
}
printf("队列长度为%d\n", QueueLength(Q));
printf("现在队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
printf("现在队列中的元素为\n");
QueueTraverse(Q, print2);
DeQueue(Q, d);
printf("删除队头元素%d\n", d);
printf("队列中的元素为\n");
QueueTraverse(Q, print2);
j = GetHead(Q, d);
if (j)
printf("队头元素为%d\n", d);
else
printf("无队头元素(空队列)\n");
ClearQueue(Q);
printf("清空队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
j = GetHead(Q, d);
if (j)
printf("队头元素为%d\n", d);
else
printf("无队头元素(空队列)\n");
DestroyQueue(Q);
}
运行结果:
二所示的队列顺序存储结构,在出队时,只是改变头指针的位置,不移动元素,可简化操作,节约时间。但这种队列顺序存储结构也有缺点,队列的每个存储空间自始至终只能存一个队列元素。即使这个队列元素出队后,其他的队列元素也不能占用这个存储空间。尤其在队列长度补偿,入队出队频繁的情况下,存储空间浪费较大。由于没有其他数据覆盖,当队头元素出队后,其值还保留在队列中。
三.
下面的方式克服了二的方式存储空间浪费较大的缺点,三采用循环队列:当队尾元素占据了存储空间的最后一个单元时,如再有新的元素入队,不说申请新的存储空间,而是将新元素插到存储空间的第一个单元,只要这个单元为空(元素已出队)。通过头尾指针对存储空间MAX_QSIZE求余做到这一点,形成循环队列。
在循环队列中,队尾指针可能小于队头指针。入队元素时,队尾指针加1.当队列满时,队尾指针等于队头指针,和队列空的条件一样。为了区别队满和队空,在循环队列中少用一个存储单元。也就是在存储空间为MAX_SIZE的循环队列中,最多只能存放MAX_SIZE-1个元素。这样,队列空的条件仍为队尾指针等于队头指针,队列满的条件改为(队尾指针+1)对MAX_SIZE求余等于队头指针。
#define MAX_QSIZE 5 //最大队列长度+1
struct SqQueue
{
QElemType *base;//初始化的动态分配存储空间
int front;//头指针,若队列不空,指向队列头元素
int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置
};
void InitQueue(SqQueue &Q)
{
Q.base = (QElemType*)malloc(MAX_QSIZE*sizeof(QElemType));
if (!Q.base)
exit(OVERFLOW);
Q.front = Q.rear = 0;
}
void DestroyQueue(SqQueue &Q)
{
if (Q.base)
free(Q.base);
Q.base = NULL;
Q.front = Q.rear = 0;
}
void ClearQueue(SqQueue &Q)
{
Q.front = Q.rear = 0;
}
Status QueueEmpty(SqQueue Q)
{
if (Q.front == Q.rear)
return TRUE;
else
return FALSE;
}
int QueueLength(SqQueue Q)
{
int i = (Q.rear - Q.front + MAX_QSIZE) % MAX_QSIZE;
return i;
}
Status GetHead(SqQueue Q, QElemType &e)
{
if (Q.front == Q.rear)
return FALSE;
e = Q.base[Q.front];
return OK;
}
Status EnQueue(SqQueue &Q, QElemType e)
{
if ((Q.rear + 1) % MAX_QSIZE == Q.front)//队列满
return ERROR;
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAX_QSIZE;
return OK;
}
Status DeQueue(SqQueue &Q, QElemType &e)
{
if (Q.front == Q.rear)//队列空
return ERROR;
e = Q.base[Q.rear];
Q.front = (Q.front + 1) % MAX_QSIZE;
return OK;
}
void QueueTraverse(SqQueue Q, void(*vi)(QElemType))
{
int i;
i = Q.front;
while (i != Q.rear)
{
vi(Q.base[i]);
i = (i + 1) % MAX_QSIZE;
}
printf("\n");
}
void print3(QElemType i)
{
printf("%d", i);
}
void main()
{
Status j;
int i = 0, l;
QElemType d;
SqQueue Q;
InitQueue(Q);
printf("初始化队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
printf("请输入整形队列元素(不超过%d个),-1为提前结束符:", MAX_QSIZE - 1);
do
{
scanf("%d", &d);
if (d == -1)
break;
i++;
EnQueue(Q, d);
} while (i < MAX_QSIZE - 1);
printf("队列长度为%d\n", QueueLength(Q));
printf("现在队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
printf("连续%d次由队头删除元素,队尾插入元素:\n", MAX_QSIZE);
for (l = 1; l <= MAX_QSIZE; l++)
{
DeQueue(Q, d);
printf("删除的元素是%d,请输入待插入的元素:", d);
scanf("%d", d);
EnQueue(Q, d);
}
l = QueueLength(Q);
printf("现在队列中的元素为\n");
QueueTraverse(Q, print3);
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:空 2:否)\n", QueueEmpty(Q));
DestroyQueue(Q);
}
运行结果:
三所采用的循环顺序队列存储结构,当队列的长度大于MAX_QSIZE-1时,无法动态的增加存储空间,原因是MAX_QSIZE是固定的常量。为了使循环队列也能动态的增加存储空间,不固定队列长度,把队列长度也作为结构体的一个成员。
四.
int QueueLength(SqQueue2 Q)
{
int i;
i = (Q.rear - Q.front + Q.queuesize) % Q.queuesize;
return i;
}
void EnQueue(SqQueue2 &Q, QElemType e)
{
int i;
if ((Q.rear + 1) % Q.queuesize == Q.front)
{
//队列满,增加存储单元
Q.base = (QElemType*)realloc(Q.base, (Q.queuesize + QUEUE_INCREMENT)*sizeof(QElemType));
if (!Q.base)
exit(ERROR);
if (Q.front > Q.rear)//形成循环
{
for (i = Q.queuesize - 1; i >= Q.front; i--)
Q.base[i + QUEUE_INCREMENT] = Q.base[i];//移动高端元素到新的高端
Q.front += QUEUE_INCREMENT;//移动队头指针
}
Q.queuesize += QUEUE_INCREMENT;//增加队列长度
}
Q.base[Q.rear] = e;//将e插入队尾
Q.rear = ++Q.rear%Q.queuesize;//移动队尾指针
}
Status DeQueue(SqQueue2 &Q, QElemType &e)
{
if (Q.rear == Q.front)
return ERROR;
e = Q.base[Q.front];//用e返回队头元素
Q.front = ++Q.front%Q.queuesize;//移动队头指针
return OK;
}
void QueueTraverse(SqQueue2 Q, void(*vi)(QElemType))
{
int i = Q.front;//i指向队头
while (i != Q.rear)//没到队尾
{
vi(Q.base[i]);
i = ++i%Q.queuesize;//向后移动i指针
}
printf("\n");
}
void main()
{
Status j;
int i, n = 11;
QElemType d;
SqQueue2 Q;
InitQueue(Q);
printf("初始化队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
printf("队列的长度为%d\n", QueueLength(Q));
printf("请输入%d个整形队列元素:\n", n);
for (i = 0; i < n; i++)
{
scanf("%d", &d);
EnQueue(Q, d);
}
printf("队列长度为%d\n", QueueLength(Q));
printf("现在队列空否?%u(1:空 2:否)\n"QueueEmpty(Q));
printf("现在队列中的元素为\n");
QueueTraverse(Q, print1);
for (i = 0; i <= 3; i++)
{
DeQueue(Q, d);
}
printf("由队头删除3个元素,最后删除的元素为%d\n", d);
printf("现在队列中的元素为\n");
QueueTraverse(Q, print1);
j = GetHead(Q, d);
if (j)
printf("队头元素为%d\n", d);
else
printf("无队头元素(空队列)\n");
for (i = 1; i <= 5; i++)
EnQueue(Q, i);
printf("依次向队尾插入1-5,现在队列中的元素为\n");
QueueTraverse(Q, print1);
ClearQueue(Q);
printf("清空队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
j = GetHead(Q, d);
if (j)
printf("队头元素为%d\n", d);
else
printf("无队头元素(空队列)\n");
DestroyQueue(Q);
}
运行结果:
五.利用非循环顺序队列采用广度搜索法求解迷宫问题(一条路径)
#define D 8//移动方向数,只能取4和8(8个可斜行;4个只可直走)
struct PosType//迷宫坐标位置类型
{
int x;//行值
int y;//列值
};
typedef struct
{
PosType seat;//当前点的行值、列值
int pre;//前一点在队列中的序号
}QElemType,SElemType;//栈元素和队列元素
struct
{
int x, y;
}move[D] = {
#if D == 8
{ 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 }, { -1,-1 }, { -1,0 }, { -1,1 }};
#endif
#if D == 4
{ 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }};
#endif
void Path()
{
//广度搜索法求一条迷宫路径
SqQueue2 q;//采用非循环顺序队列
QElemType qf, qt;//当前结点和下一点
SqStack s;//采用顺序栈
int i, flag = 1;//当找到出口,flag=0
qf.seat.x = begin1.x;//将入口作为当前点
qf.seat.y = begin1.y;
qf.pre = -1;//设入口(第一点)的上一点的序号=-1
m[qf.seat.x][qf.seat.y] = -1;//初始点设为-1(标记已访问过)
InitQueue(q);
EnQueue(q, qf);//起点入队
while (!QueueEmpty(q) && flag)
{
//队列中还有没被广度搜索过的点且还没找到出口
DeQueue(q, qf);//出队qf为当前点
for (i = 0; i < D; i++)
{
//向各个方向尝试
qt.seat.x = qf.seat.x + move[i].x;//下一个点的坐标
qt.seat.y = qf.seat.y + move[i].y;
if (m[qt.seat.x][qt.seat.y] == 1)
{
//此点是通道且不曾被访问过
m[qt.seat.x][qt.seat.y] = -1;//标记已访问过
qt.pre = q.front - 1;//qt的前一点处于队列中现队头减1的位置(没删除)
EnQueue(q, qt);//入队qt
if (qt.seat.x == end.x && qt.seat.y == end.y)//到达终点
{
flag = 0;
break;
}
}
}
}
if (flag)//搜索完整个队列还没到达终点
printf("没有路径可到达终点!\n");
else
{
//到达终点
InitStack(s);//初始化s栈
i = q.rear - 1;//i为待入栈元素在队列中的位置
while (i >= 0)//没到入口
{
Push(s, q.base[i]);//将队列中的路径入栈(栈底为出口,栈顶为入口)
i = q.base[i].pre;//i为前一元素在队列中的位置
}
i = 0;//i为走出迷宫的足迹
while (!StackEmpty(s))
{
Pop(s, qf);//依照由入口到出口的顺序弹出路径
i++;
m[qf.seat.x][qf.seat.y] = i;//标记路径为足迹(标记前的值为-1)
}
printf("走出迷宫的一个方案:\n");
Print();//输出m数组
}
}
void main()
{
Init(1);//初始化迷宫,通道值为1
Path();//求一条迷宫路径
}
运行结果: