队列的定义
队列是一种常用,重要的数据结构。它是一种只允许在一端进行插入操作(入队),在另一端进行删除操作(出队)的运算受到限制线性表,简称“队”。与同线性表相同,逻辑结构仍是一对一的关系。队列采取一种先进先出的数据结构(FIFO结构)。队列存在两种存储结构:顺序存储结构和链式存储结构。
队列如同排队打饭,先排的人先打饭,然后出队,后来的人排后面,后打饭。
顺序队列
队列的顺序表示
顺序队列是用一组地址连续的存储单元,依次存放从队头到队尾的数据元素。队列表示中需要两个指针——头指针front和尾指针rear,分别指向队头元素和队尾元素。定义如下:
typedef struct {
int* base;
int front;
int rear;
}SqQueue;
假溢出
尾指针rear移动到我们可以进行队列操作的范围之外去了,我们称呼作为队列用的存储区还没有满,但队列却发生了溢出,我们把这种现象称为"假溢出"。
首先我们有一个顺序队列(上图),这个队列的大小为5,其已经包含了四个元素data1,data2,data3,data4,接着,我们对这个队列进行出队操作,出队2个元素,队列就变成(下图)
那么我们接着再进行入队操作,我们入队2个元素,分别是data5和data6,此时我们已经发现问题了,尾指针移动到我们可以进行队列操作的范围之外去了,而1,2位置却没有存储,这就是假溢出。
循环队列
为了解决“假溢出”现象,我们的方法就是将顺序队列的数组看成一个头尾相接的循环结构。称为循环队列。
但这就有一个问题:当rear==front时,该是判满还是判空呢?
解决方案:
方案1.另外设计一个标志区别队空队满。
方案2.设一个变量,统计元素个数。
方案3:少用一个元素空间。当队尾指针指的空闲单元的后继单元是队头元素所在单元时,队满。
我们这里采取方案3,则有:
队满:(Q.rear+1)%MAXSIZE==Q.front;
队空:Q.rear==Q.front;
循环队列的操作
初始化
采用动态分配
void InitQueue(SqQueue& Q) {
Q.base = (int*)malloc(MAXSIZE*sizeof(int));
Q.front = 0;
Q.rear = 0;
return;
}
求队列长度
int QueueLength(SqQueue Q) {
return((Q.rear - Q.front + MAXSIZE) % MAXSIZE);
}
入队
void EnterQueue(SqQueue& Q, int e) {
if ((Q.rear + 1) % MAXSIZE == Q.front) {
printf("ERROR");
}
else
{
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXSIZE;
}
return;
}
出队
void DeleteQueue(SqQueue& Q, int& e) {
if (Q.front == Q.rear) {
printf("ERROR");
}
else
{
e = Q.base[Q.front];
}
Q.front = (Q.front + 1) % MAXSIZE;
return;
}
链队
若用户无法估计所用队列长度,采用链队列,不需要担心溢出情况。定义如下:
typedef struct Qnode{
int data;
struct Qnode* next;
}Qnode,*QueuePtr;
同时一个链队列需要头指针和尾指针,故还需定义如下:
typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
初始化
void InitQueueLink(LinkQueue& Q) {
Q.front = (QueuePtr)malloc(sizeof(Qnode));
Q.rear = (QueuePtr)malloc(sizeof(Qnode));
Q.front->next = NULL;
return;
}
销毁链队
void DestoryQueue(LinkQueue& Q) {
while (Q.front)
{
Q.rear = Q.front->next;
free(Q.rear);
Q.front = Q.rear;
}
return;
}
入队
void EnterQueueLink(LinkQueue& Q, int e) {
QueuePtr p = (QueuePtr)malloc(sizeof(Qnode));
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return;
}
出队
void DeleteQueueLink(LinkQueue& Q, int& e) {
QueuePtr p = (QueuePtr)malloc(sizeof(Qnode));
p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if (Q.rear == p) {
Q.rear = Q.front;
}
free(p);
return;
}