一.队列
1.队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出(First In First ut)的表简FIF。的一端称为队尾,允许删除的一端称为队头。假设队列是 q= ( a1,a2·....,a),那么a1 就是队头元素,而 an 是队尾元素。这样我们就可以删除时,总是从 ai 开始,而插入时,列在最后。这也比较符合我们通常生活中的习惯,排在第一个的优先出列,最后来的当然排在队伍最后, 如图所示:
队列存储结构的实现有以下两种方式:
①顺序队列:在顺序表的基础上实现的队列结构。
②链队列:在链表的基础上实现的队列结构。
两者的区别仅是顺序表和链表的区别,即在实际的物理空间中,数据集中存储的队列是顺序队列,分散存储的队列是链队列。
2.队列如何设计
队列只有链式的设计方法,其本身分为多种队列,如顺序队列和循环队列,还有衍生的优先队列等等,以顺序队列的设计为例。
首先是队列的结点设计,可以设计出两个结构体,一个结构体 Node 表示结点,其中包含有 data 域和 next 指针,如图:
其中 data 表示数据,其可以是简单的类型,也可以是复杂的结构体。next 指针表示,下一个的指针,其指向下一个结点,通过 next 指针将各个结点链接。
添加一个结构体,其包括了两个分别永远指向队列的队尾和队首的指针。
3.队列的抽象数据类型:
InitQueue:初始化操作,建立一个空队列 Q。
DestroyQueue:若队列Q存在,则销毁它。
ClearQueue :将队列Q清空。
QueueEmpty:若队列Q为空,返回true,否则返回 false。
GetHead:若队列Q存在且非空,用e返回队列Q的队头元素。
EnQueue:若队列Q存在,插入新元素e 到队列Q中并成为队尾元素。
DeQueue:删除队列Q中队头元素,并用e返回其值。
QueueLength:返回队列Q的元素个数。
#define OK 1
#define ERROR 0
#define OVERFLOW -1
//#define MAXQSIZE 100 //最大队列长度
typedef int Status;
const int MaxQSize = 100;
typedef int ElemType;
typedef struct
{
ElemType* base; //初始化的动态分配存储空间
int front; //头指针,若队列不空,指向队列头元素
int rear; //尾指针,若队列不空,指向队列尾元素的下一个位置
int cursize; //循环队列元素的个数, 为0时队列空,当cursize == MAXQSIZE 时队列满
int maxsize;
}SeqQueue;
//构造一个空队列
Status InitQueue(SeqQueue* pe)
{
assert(pe != nullptr);
pe->base = (ElemType*)malloc(sizeof(ElemType) * MaxQSize);
if (nullptr == pe->base)
{
exit(EXIT_FAILURE);
}
pe->cursize = 0;
pe->front = 0;
pe->rear = 0;
pe->maxsize = MaxQSize;
return OK;
}
//销毁队列,不再存在
void DestroyQueue(SeqQueue* pe)
{
assert(pe != nullptr);
free(pe->base);
pe->base = nullptr;
pe->front = pe->rear = pe->cursize = pe->maxsize = 0;
}
//将队列清为空
void ClearQueue(SeqQueue* pe)
{
assert(pe != nullptr);
pe->cursize = 0;
pe->front = 0;
pe->rear = 0;
}
//返回队列的元素个数,即为队列的长度
int QueueLength(const SeqQueue* pe)
{
assert(pe != nullptr);
return pe->cursize;
}
//检查循环队列是否为空
bool IsEmpty(const SeqQueue* pe) {
assert(pe != nullptr);
return pe->cursize == 0;
}
// 检查循环队列是否已满。
bool IsFull(const SeqQueue* pe) {
assert(pe != nullptr);
return pe->cursize == pe->maxsize;
}
//若队列不空,则用pval返回队头元素,并返回OK;否则返回ERROR
Status GetHead(const SeqQueue* pe, ElemType* pval)
{
assert(pe != nullptr && pval != nullptr);
if (IsEmpty(pe))
{
return ERROR;
}
*pval = pe->base[pe->front];
return OK;
}
//若队列不空,则用pval返回的队尾元素,并返回OK;否则返回ERROR
Status GetTail(const SeqQueue* pe, ElemType* pval)
{
assert(pe != nullptr && pval != nullptr);
if (IsEmpty(pe))
{
return ERROR;
}
int rindex = pe->rear - 1;
if (rindex < 0) rindex = pe->maxsize - 1;
}
//队尾插入元素val, val为新的队尾元素
Status EnQueue(SeqQueue* pe, ElemType val)
{
assert(pe != nullptr);
if (IsFull(pe))
{
return ERROR;
}
pe->base[pe->rear] = val;
pe->rear = (pe->rear + 1) % pe->maxsize;
pe->cursize += 1;
return OK;
}
//若队列不空,则删除队头元素,用pval返回其值,并返回0K,否则返回ERROR
Status DeQueue(SeqQueue* pe, ElemType* pval)
{
assert(pe != nullptr && pval != nullptr);
if (IsEmpty(pe))
{
return ERROR;
}
*pval = pe->base[pe->front];
pe->front = (pe->front + 1) % pe->maxsize;
return OK;
}
int main()
{
SeqQueue seq;
InitQueue(&seq);
DestroyQueue(&seq);
return 0;
}
二.链队列
用链表表示的队列简称为链队列。一个链队列显然需要两个分别指示队头和队尾的指针(分别称为头指针和尾指针)才能惟一确定。这里,和线性表的单链表一样,为了操作方便起见,我们也给链队列添加一个头结点,并令头指针指向头结点。 由此,空的链队列的判决条件为头指针和尾指针均指向头结点。
链队列的基本操作及其代码展示:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define OK 0
#define ERROR -1
typedef int Status;
typedef int QElemType;
typedef struct QNode
{
QElemType data;
struct QNode* next;
}QNode, * QueuePtr;
typedef struct
{
QueuePtr front; // 对头指针
QueuePtr rear; // 队尾指针
int cursize; // 元素个数
}LinkQueue;
QNode* Buynode(QElemType val, QNode* narg = nullptr) //购买节点 数据域 next域
{
QNode* s = (QNode*)malloc(sizeof(QNode)); //购买QNode结点
if (nullptr == s) //malloc不一定成功
{
exit(EXIT_FAILURE); // _exit; abort(); exit终止当前的进程
}
s->data = val;
s->next = narg;
return s;
}
void Freenode(QNode* p)
{
assert(p != nullptr);
free(p); //释放结点
}
//构造一个空队列
Status InitQueue(LinkQueue* pe)
{
assert(pe != nullptr);
pe->cursize = 0;
pe->front = pe->rear = Buynode(0);
return OK;
}
//将队列清为空
void ClearQueue(LinkQueue* pe)
{
assert(pe != nullptr);
while (pe->front->next != nullptr)
{
QNode* q = pe->front->next;
pe->front->next = q->next;
Freenode(q);
}
}
//销毁队列,不再存在
void DestroyQueue(LinkQueue* pe)
{
assert(pe != nullptr);
ClearQueue(pe);
Freenode(pe->front);
pe->front = pe->rear = nullptr;
}
//返回队列的元素个数,即为队列的长度
int QueueLength(const LinkQueue* pe)
{
assert(pe != nullptr);
return pe->cursize;
}
//检查队列是否为空
bool IsEmpty(const LinkQueue* pe)
{
assert(pe != nullptr);
return pe->cursize == 0;
}
//若队列不空,则用pval返回队头元素,并返回OK;否则返回ERROR
Status GetHead(const LinkQueue* pe, QElemType* pval)
{
assert(pe != nullptr);
if (IsEmpty(pe))
{
return ERROR;
}
*pval = pe->front->next -> data;
return OK;
}
//若队列不空,则删除队头元素,用pval返回其值,并返回0K,否则返回ERROR
Status DeQueue(LinkQueue* pe, QElemType* pval)
{
assert(pe != nullptr && pval != nullptr);
if (IsEmpty(pe))
{
return ERROR;
}
QNode* q = pe->front->next; // first;
*pval = q->data;
pe->front->next = q->next;
Freenode(q);
pe->cursize -= 1;
if (pe->cursize == 0)
{
pe->rear = pe->front;
}
return OK;
}
int main()
{
LinkQueue myq;
InitQueue(&myq);
int x;
while (!IsEmpty(&myq))
{
DeQueue(&myq, &x);
printf("%d\n", x);
}
DestroyQueue(&myq);
return 0;
}