赞
踩
队列(queue)是一种只允许在一端进行插入操作,而在另一端进行删除操作的线性表。其严格遵循先进先出(First In First Out)的规则,简称FIFO。
队列与栈类似,实现方式有两种。一种是以数组的方式实现,另一种以单链表来实现。这两种实现方式各有优劣,并且都有细节需要处理。
问题一:在不断出队与进队得到过程中,起始下标与末尾下标都在向后移动,当两个下标同时指向数组末尾时就无法再移动了,并且**浪费前面大量空间,**如图1
问题二:为了解决上述问题,我们将数组首尾相接变为循环数组。但这时又会出现一个问题,那便是当队首与队尾下标指向同一个节点时,这个队列到底是空还是满呢?这时我们有三个解决方法
第一种:牺牲一个单元来区分队空和队满,这时若队列不为空,让队尾下标指向队尾的下一个位置。约定以队头指针在队尾指针的下一位置作为队满的标志,即Q->rear+1==Q->front
。如图二。
第二种:增设表示元素个数的数据成员 size
。这样,队空的条件为 Q->size==0
;队满的条件为 Q->size==MaxSize
。
第三种:增加表示队满的数据成员flag
。将flag初始化为0,当队满时将其置为1。
- 队列的初始化。
- 判断队列是否为空。
- 判断队列是否满了。
- 返回队头与队尾的元素。
- 返回队列的大小。
- 入队与出队。
- 打印队列的元素。
- 销毁队列。
链式队的声明十分简单,参照上面图我们就可以直接实现了。
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* front;
QNode* rear;
size_t size;
}Queue;
根据上述分析,我们采用一个数组来实现队列,其声明如下
typedef int QDataType;
#define MAXSIZE 50 //定义元素的最大个数
/*循环队列的顺序存储结构*/
typedef struct {
QDataType *data;
int front; //头指针
int rear; //尾指针
}Queue;
对队列声明的数据进行初始化,防止随机值。
void QueueInit(Queue* q)
{
q->front = NULL;
q->rear = NULL;
q->size = 0;
}
void QueueInit(Queue* q)
{
q->data = (QDataType*)malloc(sizeof(QDataType )* MAXSIZE);
if (q->data == NULL)
{
perror("malloc fail:");
return;
}
q->front = 0;
q->rear = 0;
}
判断队列是否为空十分简单,这里就不在赘述。
bool QueueEmpty(Queue* q)
{
assert(q);
return (q->front == NULL) && (q->rear == NULL);
}
bool QueueEmpty(Queue*q)
{
assert(q);
return q->front == q->rear;
}
链式队不需要判断是否为满的情况。
为了完成循环操作,需要对其进行取模操作,防止越界。
bool QueueFull(Queue*q)
{
assert(q);
return q->front == (q->rear+1)%MAXSIZE;
}
因为定义了头指针与尾指针,所以访问数据也十分方便。
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->front->data;
}
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->rear->data;
}
rear
下标可能指向下标0,这时贸然减一就可能出现越界的情况。为了解决这个问题我们可以加上MAXSIZE
再对其取模。
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->data[q->front];
}
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->data[(q->rear-1+MAXSIZE)%MAXSIZE];
}
size_t QueueSize(Queue* q)
{
return q->size;
}
求循环队列的大小,我们很容易想到用Q->rear-Q->front
得出队列元素个数。但是我们要考虑到一种特殊情况:当队列先删除元素再添加元素时,末尾下标**rear**
可能循环重置,如下图。
那到底该如何解决这个问题呢?其实我们只需要在原来基础上加上一个MAXSIZE
就行了,为了使图一情况也适用我们仍需模上一个MAXSIZE
。
size_t QueueSize(Queue*q)
{
assert(q);
return (q->rear - q->front + MAXSIZE) % MAXSIZE;
}
链式队列入队时需要判断队列是否为空的特殊情况,如果是则还需要将尾指针也指向这个节点。
void QueuePush(Queue* q, QDataType x) { assert(q); QNode* newnode = (QNode*)malloc(sizeof(QNode)); newnode->data = x; newnode->next = NULL; if (newnode == NULL) { perror("malloc fail"); return; } if (q->front == NULL) { q->front = q->rear = newnode; } else { q->rear->next = newnode; q->rear = newnode; } q->size++; }
为了使循环队列在插入数据时实现循环操作,我们可以每次进行取模操作。并且每次入队之前要检查循环队是否已满。
void QueuePush(Queue* q, QDataType x)
{
assert(q);
if(QueueFull)
{
printf("队列已满\n");
return ;
}
q->data[q->rear] = x;
q->rear = (q->rear + 1) % MAXSIZE; //rear指针向后移一位置,若到最后则转到数组头部
}
同样考虑特殊情况,防止队列为空。并且当队列只有一个节点时需要将头指针与尾指针都置为空。
void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); //1.只有一个结点 if (q->front == q->rear) { free(q->front); q->front = q->rear = NULL; } //2.有多个结点 else { QNode* del = q->front; q->front = q->front->next; free(del); del = NULL; } q->size--; }
同样为了实现循环,我们可以进行取模操作。
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
q->front = (q->front + 1) % MAXSIZE; //front指针向后移一位置,若到最后则转到数组头部
}
void QueuePrint(Queue* q)
{
assert(q);
QNode* cur = q->front;
QNode* tail = q->rear;
printf("队头->");
while (cur != tail->next)
{
printf("%d->",cur->data);
cur = cur->next;
}
printf("队尾\n");
}
void QueuePrint(Queue* q)
{
assert(q);
int cur = q->front;
printf("队头->");
while (cur != q->rear)
{
printf("%d->", q->data[cur]);
cur = (cur + 1) % MAXSIZE;
}
printf("队尾\n");
}
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
del = NULL;
}
q->front = q->rear = NULL;
}
void QueueDestroy(Deque* q)//销毁队列
{
assert(q);
free(q->data);
q->data = NULL;
q->front = q->rear = 0;
}
对比项 | 链式队 | 循环队列 |
---|---|---|
时间效率 | 因为存在头指针与尾指针,所以链式队的出队与入队的时间都相对较小。 | 循环队列是基于数组实现的,支持下标的随机访问,所以时间消耗也并不大 |
空间效率 | 链式队每次入队都需固定创造一个新的节点,空间利用率较高,较稳定。 | 循环队列的空间是固定的,可能会造成空间的浪费。 |
队列的应用与栈一样,十分广泛
- 当我们去食堂扫码订餐时,你的订单就会加入一个队列中。
- 在操作系统中,队列可以用来管理任务进度与进程切换。
#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<stdbool.h> typedef int QDataType; typedef struct QueueNode { QDataType data; struct QueueNode* next; }QNode; typedef struct Queue { QNode* front; QNode* rear; size_t size; }Queue; void QueueInit(Queue* q);//初始化队列 bool QueueEmpty(Queue* q);//判断是否为空 QDataType QueueFront(Queue* q);//获取队头元素 QDataType QueueBack(Queue* q);//或许队尾元素 size_t QueueSize(Queue* q);//或许队列长度 void QueuePush(Queue* q, QDataType x);//入队 void QueuePop(Queue* q);//出队 void QueuePrint(Queue* q);//打印队列元素 void QueueDestroy(Queue* q);//销毁队列
#include"Queue.h" void QueueInit(Queue* q) { q->front = NULL; q->rear = NULL; q->size = 0; } bool QueueEmpty(Queue* q) { assert(q); return (q->front == NULL) && (q->rear == NULL); } QDataType QueueFront(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->front->data; } QDataType QueueBack(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->rear->data; } size_t QueueSize(Queue* q) { return q->size; } void QueuePush(Queue* q, QDataType x) { assert(q); QNode* newnode = (QNode*)malloc(sizeof(QNode)); newnode->data = x; newnode->next = NULL; if (newnode == NULL) { perror("malloc fail"); return; } if (q->front == NULL) { q->front = q->rear = newnode; } else { q->rear->next = newnode; q->rear = newnode; } q->size++; } void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); //1.只有一个结点 if (q->front == q->rear) { free(q->front); q->front = q->rear = NULL; } //2.有多个结点 else { QNode* del = q->front; q->front = q->front->next; free(del); del = NULL; } q->size--; } void QueuePrint(Queue* q) { assert(q); QNode* cur = q->front; QNode* tail = q->rear; printf("队头->"); while (cur != tail->next) { printf("%d->",cur->data); cur = cur->next; } printf("队尾\n"); } void QueueDestroy(Queue* q) { assert(q); QNode* cur = q->front; while (cur) { QNode* del = cur; cur = cur->next; free(del); del = NULL; } q->front = q->rear = NULL; }
#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<stdbool.h> typedef int QDataType; #define MAXSIZE 50 //定义元素的最大个数 /*循环队列的顺序存储结构*/ typedef struct { QDataType *data; int front; //头指针 int rear; //尾指针 }Queue; void QueueInit(Queue* q);//初始化队列 bool QueueEmpty(Queue* q);//判断是否为空 bool QueueFull(Queue*q);//判断队列是否满 QDataType QueueFront(Queue* q);//获取队头元素 QDataType QueueBack(Queue* q);//或许队尾元素 size_t QueueSize(Queue* q);//或许队列长度 void QueuePush(Queue* q, QDataType x);//入队 void QueuePop(Queue* q);//出队 void QueuePrint(Queue* q);//打印队列元素
void QueueInit(Queue* q) { q->data = (QDataType*)malloc(sizeof(QDataType )* MAXSIZE); if (q->data == NULL) { perror("malloc fail:"); return; } q->front = 0; q->rear = 0; } bool QueueEmpty(Queue*q) { assert(q); return q->front == q->rear; } bool QueueFull(Queue*q) { assert(q); return q->front == (q->rear+1)%MAXSIZE; } QDataType QueueFront(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->data[q->front]; } QDataType QueueBack(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->data[(q->rear-1+MAXSIZE)%MAXSIZE]; } size_t QueueSize(Queue*q) { assert(q); return (q->rear - q->front + MAXSIZE) % MAXSIZE; } void QueuePush(Queue* q, QDataType x) { assert(q); if(QueueFull)//判断队列是否满了 { printf("队列已满\n"); return ; } q->data[q->rear] = x; q->rear = (q->rear + 1) % MAXSIZE; //rear指针向后移一位置,若到最后则转到数组头部 } void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); q->front = (q->front + 1) % MAXSIZE; //front指针向后移一位置,若到最后则转到数组头部 } void QueuePrint(Queue* q) { assert(q); int cur = q->front; printf("队头->"); while (cur != q->rear) { printf("%d->", q->data[cur]); cur = (cur + 1) % MAXSIZE; } printf("队尾\n"); } void QueueDestroy(Deque* q)//销毁队列 { assert(q); free(q->data); q->data = NULL; q->front = q->rear = 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。