1. 顺序存储
在使用队列时,我们使用两个变量表示队列的头和尾。
以长度为5的顺序队列为例:
- 开始队列头
Q.front
和队列尾Q.rear
相等为0,且在数组头部; - C1、C2、C3入队,
Q.front
不动,Q.rear
指向3。 - C1、C2出队,
Q.rear
不动,Q.front
指向2。
之前使用过两个位置,我们无法访问,造成了资源的浪费。
如果将Q.front
和Q.rear
范围的数据移动倒表头,可以解决这个问题,但是这样就太麻烦了。
所以提出了循环队列的概念。
当Q.rear
到达队尾时,会来到表头位置,如上图(a)所示。
但是这样又有了一个新问题:
- 空队列时,
Q.front
和Q.rear
相等,如上图(c)所示。 - 当队列被占满,
Q.front
和Q.rear
也相等,如上图(b)所示。
这样就没法判断队列是空队列还是满队列了。
这里,我们牺牲一个存储单元,当Q.rear
和Q.front
之间只空一个存储单元时认为队列是满的,就可以解决这个问题。
1.1 结构定义
#define MAXSIZE 10
typedef int Status;
typedef int QElemType;
typedef struct
{
QElemType data[MAXSIZE];
int front;
int rear;
} SqQueue;
1.2 函数实现
// 1.构建一个空队列S
Status InitQueue(SqQueue *Q) {
Q->front = Q->rear = 0;
return OK;
}
// 2.将队列置空
Status ClearQueue(SqQueue *Q) {
Q->front = Q->rear = 0;
return OK;
}
// 3.判断队列是否为空
Status QueueEmpty(SqQueue Q) {
if (Q.rear == Q.front)
return TRUE;
else
return FALSE;
}
// 4.返回队列的长度
int QueueLength(SqQueue Q){
return (Q.rear + MAXSIZE - Q.front) % MAXSIZE;
}
// 5.获取队列头元素
Status QueueFront(SqQueue Q, QElemType *e){
if (Q.rear == Q.front) // 队列是空的
return ERROR;
else
*e = Q.data[Q.front];
return OK;
}
// 6.入队
Status Enqueue(SqQueue *Q, QElemType e){
if ((Q->rear + 1) % MAXSIZE == Q->front) return ERROR;//队列满了
Q->data[Q->rear] = e;
Q->rear = (Q->rear + 1) % MAXSIZE;
return OK;
}
// 7.出队
Status Dequeue(SqQueue *Q, QElemType *e){
if (Q->rear == Q->front) return ERROR; // 队列是空的
*e = Q->data[Q->front];
Q->front = (Q->front + 1) % MAXSIZE;
return OK;
}
// 8.从队列头开始遍历
Status QueueTraverse(SqQueue Q){
int i = Q.front;
while (i != Q.rear) {
printf("%d ", Q.data[i]);
i = (i + 1) % MAXSIZE;
}
printf("\n");
return OK;
}
int main(int argc, const char * argv[]) {
SqQueue Q;
int e;
if (InitQueue(&Q) == OK) {
for (int j = 1 ; j < 11; j++) {
Enqueue(&Q, j);
}
}
printf("循环队列中元素为: \n");
QueueTraverse(Q);
printf("队列长度: %d\n", QueueLength(Q));
Dequeue(&Q, &e);
printf("出队: %d\n", e);
QueueTraverse(Q);
printf("队列长度: %d\n", QueueLength(Q));
Enqueue(&Q, e);
printf("入队: %d\n", e);
QueueTraverse(Q);
printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
QueueFront(Q, &e);
printf("队列头元素: %d \n", e);
printf("队列长度: %d\n", QueueLength(Q));
ClearQueue(&Q);
printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
printf("队列长度: %d\n", QueueLength(Q));
return 0;
}
// 输出
循环队列中元素为:
1 2 3 4 5 6 7 8 9
队列长度: 9
出队: 1
2 3 4 5 6 7 8 9
队列长度: 8
入队: 1
2 3 4 5 6 7 8 9 1
是否为空队列: 0 (1:空 0:否)
队列头元素: 2
队列长度: 9
是否已经清空队列 1 (1:空 0:否)
队列长度: 0
2. 链式存储
链式存储不会遇到顺序存储的问题,所以直接使用单向链表就可以实现。
2.1 结构定义
typedef int Status;
typedef int QElemType;
/* 链栈结构 */
typedef struct QueueNode
{
QElemType data;
struct QueueNode *next;
} QueueNode, *QueueNodePtr;
typedef struct
{
QueueNodePtr front;
QueueNodePtr rear;
int count;
} LinkQueue;
2.2 函数实现
无头结点
// 1.构建一个空队列S
Status InitQueue(LinkQueue *Q) {
if (!Q) return ERROR;
Q->front = Q->rear = NULL;
Q->count = 0;
return OK;
}
// 2.将队列置空
Status ClearQueue(LinkQueue *Q) {
if (!Q) return ERROR;
QueueNodePtr p, q;
p = Q->front;
while (p) {
q = p;
p = p->next;
free(q);
}
Q->front = Q->rear = NULL;
Q->count = 0;
return OK;
}
// 3.判断队列是否为空;
Status QueueEmpty(LinkQueue Q) {
if (!Q.front)
return TRUE;
else
return FALSE;
}
// 4.返回队列的长度
int QueueLength(LinkQueue Q) {
return Q.count;
}
// 5.获取队列头元素
Status QueueFront(LinkQueue Q, QElemType *e) {
if (!Q.front) // 队列是空的
return ERROR;
else
*e = Q.front->data;
return OK;
}
// 6.入队
Status Enqueue(LinkQueue *Q, QElemType e) {
if (!Q) return ERROR;
QueueNodePtr node = (QueueNodePtr)malloc(sizeof(QueueNode));
if (!node) return ERROR; //没有空间了
node->data = e;
node->next = NULL;
if (!Q->front) Q->front = node;
if (Q->rear) Q->rear->next = node;
Q->rear = node;
Q->count++;
return OK;
}
// 7.出队
Status Dequeue(LinkQueue *Q, QElemType *e) {
if (!Q || !Q->front) return ERROR; // 队列是空的
QueueNodePtr node = Q->front;
Q->front = node->next;
if (!Q->front) Q->rear = NULL; // 队列空了
Q->count--;
*e = node->data;
free(node);
return OK;
}
// 8.从队列头开始遍历
Status QueueTraverse(LinkQueue Q) {
QueueNodePtr node = Q.front;
while (node) {
printf("%d ", node->data);
node = node->next;
}
printf("\n");
return OK;
}
int main(int argc, const char * argv[]) {
LinkQueue Q;
int e;
if (InitQueue(&Q) == OK) {
for (int j = 1 ; j < 11; j++) {
Enqueue(&Q, j);
}
}
printf("链式队列中元素为: \n");
QueueTraverse(Q);
printf("队列长度: %d\n", QueueLength(Q));
Dequeue(&Q, &e);
printf("出队: %d\n", e);
QueueTraverse(Q);
printf("队列长度: %d\n", QueueLength(Q));
Enqueue(&Q, e);
printf("入队: %d\n", e);
QueueTraverse(Q);
printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
QueueFront(Q, &e);
printf("队列头元素: %d \n", e);
printf("队列长度: %d\n", QueueLength(Q));
ClearQueue(&Q);
printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
printf("队列长度: %d\n", QueueLength(Q));
return 0;
}
// 输出
链式队列中元素为:
1 2 3 4 5 6 7 8 9 10
队列长度: 10
出队: 1
2 3 4 5 6 7 8 9 10
队列长度: 9
入队: 1
2 3 4 5 6 7 8 9 10 1
是否为空队列: 0 (1:空 0:否)
队列头元素: 2
队列长度: 10
是否已经清空队列 1 (1:空 0:否)
队列长度: 0
有头节点
// 1.构建一个空队列S
Status InitQueue(LinkQueue *Q) {
if (!Q) return ERROR;
Q->front = Q->rear = (QueueNodePtr)malloc(sizeof(QueueNode)); //生成头结点
if (!Q->front) return ERROR;
Q->front->next = NULL;
return OK;
}
// 2.销毁队列(要包括头结点)
Status DestoryQueue(LinkQueue *Q) {
if (!Q) return ERROR;
while (Q->front) {
Q->rear = Q->front->next;
free(Q->front);
Q->front = Q->rear;
}
Q->count = 0;
return OK;
}
// 3.将队列置空(不释放头结点)
Status ClearQueue(LinkQueue *Q) {
if (!Q) return ERROR;
QueueNodePtr p, q;
Q->rear = Q->front; // 都指向头结点
p = Q->front->next; // 释放之后的节点
Q->front->next = NULL;
while (p) {
q = p;
p = p->next;
free(q);
}
Q->count = 0;
return OK;
}
// 4.判断队列是否为空;
Status QueueEmpty(LinkQueue Q) {
if (Q.front == Q.rear)
return TRUE;
else
return FALSE;
}
// 5.返回队列的长度
int QueueLength(LinkQueue Q) {
return Q.count;
}
// 6.获取队列头元素
Status QueueFront(LinkQueue Q, QElemType *e) {
if (Q.front == Q.rear) { // 队列是空的
return FALSE;
}
*e = Q.front->next->data;
return TRUE;
}
// 7.入队
Status Enqueue(LinkQueue *Q, QElemType e) {
if (!Q) return ERROR;
QueueNodePtr node = (QueueNodePtr)malloc(sizeof(QueueNode));
if (!node) return ERROR;
node->data = e;
node->next = NULL;
Q->rear->next = node;
Q->rear = node;
Q->count++;
return OK;
}
// 8.出队
Status Dequeue(LinkQueue *Q, QElemType *e) {
if (!Q || Q->front == Q->rear) return ERROR; // 队列是空的
QueueNodePtr node = Q->front->next;
Q->front->next = node->next;
Q->count--;
*e = node->data;
if (Q->rear == node) Q->rear = Q->front; // *删空后将rear指向头结点
free(node);
return OK;
}
// 9.从队列头开始遍历
Status QueueTraverse(LinkQueue Q) {
QueueNodePtr node = Q.front->next;
while (node) {
printf("%d ", node->data);
node = node->next;
}
printf("\n");
return OK;
}
int main(int argc, const char * argv[]) {
LinkQueue Q;
int e;
if (InitQueue(&Q) == OK) {
for (int j = 1 ; j < 11; j++) {
Enqueue(&Q, j);
}
}
printf("链式队列中元素为: \n");
QueueTraverse(Q);
printf("队列长度: %d\n", QueueLength(Q));
Dequeue(&Q, &e);
printf("出队: %d\n", e);
QueueTraverse(Q);
printf("队列长度: %d\n", QueueLength(Q));
Enqueue(&Q, e);
printf("入队: %d\n", e);
QueueTraverse(Q);
printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
QueueFront(Q, &e);
printf("队列头元素: %d \n", e);
printf("队列长度: %d\n", QueueLength(Q));
ClearQueue(&Q);
printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
printf("队列长度: %d\n", QueueLength(Q));
return 0;
}
// 输出
链式队列中元素为:
1 2 3 4 5 6 7 8 9 10
队列长度: 10
出队: 1
2 3 4 5 6 7 8 9 10
队列长度: 9
入队: 1
2 3 4 5 6 7 8 9 10 1
是否为空队列: 0 (1:空 0:否)
队列头元素: 2
队列长度: 10
是否已经清空队列 1 (1:空 0:否)
队列长度: 0