1.队列的定义
队列是只允许在一端进行插入,在另一端删除的线性表
特点:先进先出(FIFO)
2.队列的基本操作
InitQueue(&Q):初始化队列,构造一个空队列Q。
DestoryQueue(&Q):销毁队列。销毁并释放队列Q所占用的内存空间。
EnQueue(&Q,x):入队,若队列Q未满,将x加入,使之成为新的队尾。
DeQueue(&Q,&x):出队,若队列Q非空,删除队头元素,并用x返回。
GetHead(Q,&x):读队头元素,若队列非空,则将队头元素赋值给x。
其他常用操作:
QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回false。
3.队列的顺序实现
(1)初始化操作
此初始化为让队尾指针指向队尾元素的后一个位置
#define MaxSize 10
typedef struct{
ElemType data[MaxSize]; //用静态数组存放队列元素
int front,rear; //队头指针和队尾指针
}SqQueue;
//初始化队列
void InitQueue(SqQueue &Q){
//初始时 队头、队尾指针指向0(队尾指针指向的是队尾元素的后一个位置)
Q.rear = Q.front = 0;
}
void testQueue(){
//声明一个队列(顺序存储)
SqQueue Q;
InitQueue(Q);
//后续操作...
}
//判断队列是否为空
bool QueueEmpty(SqQueue Q){
if(Q.rear == Q.front) //队空条件
return true;
else
return false;
}
(2)入队操作
#define MaxSize 10
typedef struct{
ElemType data[MaxSize]; //用静态数组存放队列元素
int front,rear; //队头指针和队尾指针
}SqQueue;
//入队
bool EnQueue(SqQueue &Q,ElemType x){
if(队列已满)
return false; //队满则报错
Q.data[Q.rear] = x; //将x插入队尾
Q.rear = Q.rear+1; //队尾指针后移
return true;
}
(3)循环队列
取余%可以将存储空间在逻辑上变成“环状”。
#define MaxSize 10
typedef struct{
ElemType data[MaxSize];
int front,rear;
}SqQueue;
//入队
bool EnQueue(SqQueue &Q,ElemType x){
if(队列已满)
return false;
Q.data[Q.rear] = x;
Q.rear = (Q.rear+1)%MaxSize; //队尾指针加1取模
return true;
}
判断队满(不考虑是否浪费存储空间)
由于插入和删除可能交替进行,队列中可能有空余,但此判断队满时不考虑。
if ((Q.rear+1)%MaxSize == Q.front)
return false;
(4)出队操作
//出队(删除一个队头元素,并用x值返回)
bool DeQueue(SqQueue &Q,ElemType &x){
if(Q.rear == Q.front)
return false; //队空则报错
x = Q.data[Q.front];
Q.front = (Q.front+1)%MaxSize;
return true;
}
(5)查询操作
//获取队头元素的值,用x返回
bool GetHead(SqQueue Q,ElemType &x){
if(Q.rear == Q.front)
return false; //队空则报错
x = Q.data[Q.front];
return true;
}
(6)判断队满(要求不浪费存储空间)
方法一:
#define MaxSize 10;
typedef struct{
ElemType data[MaxSize];
int front,rear;
int size; //队列的长度
}SqQueue;
初始化时:rear=front=0;size=0;
插入成功size++;
删除成功size--;
队满条件:front==rear && size==MaxSize;
方法二:
#define MaxSize 10
typedef struct {
ElemType data[MaxSize];
int front,rear;
int tag; //最近进行的是删除/插入
}SqQueue;
初始化时:rear=front=0;tag=0;
每次删除操作成功时,都令tag=0;
每次插入操作成功时,都令tag=1;
队满条件:front==rear && tag==1;
(7)其他初始化方法
队尾指针指向队尾元素时,先使队尾指针加1,再进行插入删除操作。
判空:(Q.rear+1)%MaxSize == Q.front
判满:
方案1:牺牲一个存储单元(最后一个存储单元不会存进数据):(Q.rear+2)%MaxSize == Q.front;
方案2:增加辅助变量size或tag。
4.队列的链式实现
typedef struct LinkNode{ //链式队列实现
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct{ //链式队列
LinkNode *front,*rear; //队列的队头和队尾指针
}LinkQueue;
初始化(带头结点)
typedef struct LinkNode{
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front,*rear;
}LinkQueue;
//初始化队列(带头结点)
void InitQueue(LinkQueue &Q){
//初始时 front、rear 都指向头结点
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next = NULL;
}
void testLinkQueue(){
LinkQueue Q; //声明一个队列
InitQueue(Q); //初始化队列
//...后续操作...
}
判断队列是否为空
bool IsEmpty(LinkQueue Q){
if(Q.front == Q.rear)
return true;
else
return false;
}
初始化队列(不带头结点)
//初始化队列(不带头结点)
void InitQueue(LinkQueue &Q){
//初始时 front、rear 都指向NULL
Q.front = NULL;
Q.rear = NULL;
}
//判断队列是否为空(不带头结点)
bool IsEmpty(LinkQueue Q){
if(Q.front == NULL)
return true;
else
return false;
}
入队(带头结点)
//新元素入队(带头结点)
void EnQueue(LinkQueue &Q,ElemType x){
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
Q.rear->next = s; //新结点插入到rear之后
Q.rear = s; //修改表尾指针
}
入队(不带头结点)
//新元素入队(不带头结点)
void EnQueue(LinkQueue &Q,ElemType x){
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
if(Q.front == NULL){ //在空队列中插入第一个元素
Q.front = s; //修改队头指针
Q.rear = s;
}
else{
Q.rear->next = s; //新结点插入到rear结点之后
Q.rear = s; //修改rear指针
}
}
注意:不带头结点的队列,第一个元素入队时需要特别处理。
出队(带头结点)
//队头元素出队(带头结点)
bool DeQueue(LinkQueue &Q,ElemType &x){
if(Q.front == Q.rear)
return false; //空队
LinkNode *p = Q.front->next;
x = p->data; //用变量x返回队头元素
Q.front->next = p->next; //修改头结点的next指针
if(Q.rear == p) //此次是最后一个结点出队
Q.rear = Q.front; //修改rear指针
free(p); //释放结点空间
return true;
}
出队(不带头结点)
//队头元素出队(不带头结点)
bool DeQueue(LinkQueue &Q,ElemType &x){
if(Q.front == NULL)
retrun false; //空队
LinkNode *p = Q.front; //p指向此次出队的结点
x = p->data; //用变量x返回队头元素
Q.front = p->next; //修改front指针
if(Q.rear == p){ //此次是最后一个结点出队
Q.front = NULL; //front指向NULL
Q.rear = NULL; //rear指向NULL
}
free(p); //释放结点空间
return true;
}
链式队列的空间是可以拓展的。
5.双端队列
(1)双端队列:允许从两端插入、两端删除的队列
(2)输入受限的双端队列:允许从两端删除、从一端插入的队列
(3)输出受限的双端队列:允许从两端插入、从一端删除的队列