文章目录
队列的基本概念
队列的顺序存储
顺序队列
存储类型
基本操作
循序队列
存储类型
基本操作
循环队列判空与判满的三种解决方案
方法一:牺牲一个存储单元
方法二:类型增设记录型变量size
方法三:类型增设标志型变量tag
队列的链式存储
链队的存储类型
链队的基本操作
初始化
判空
入队
出队
双端队列
总结
队列的基本概念
队列(Queue)简称队,是一种操作受限的线性表。只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称入队或进队,删除元素称为出队或离队。 和生活中的排队一致,具有先进先出(First In First Out)的特性。
相关术语
- 队头(Front):允许删除的一端,又称队首
- 队尾(Rear):允许插入的一端
- 空队列:不含有任何元素的空表
基本操作
- InitQueue(&Q):初始化队列
- QueueEmpty(Q):判断队列是否为空
- EnQueue(&Q,x):入队,若队列未满,则将x加入到队列,称为新的队尾
- DeQueue(&Q,&x):出队,若队列非空,则删除队头元素,并存储在x中返回
- GeyHead(Q,&x):读取队头元素,若队列Q非空,则将队头元素赋值给x
队列的顺序存储
顺序队列
队列的顺序实现是指分配一块连续的存储单元存放在队列的元素 ,并附设两个指针:队头指针front指向队头元素,队尾指针rear指向队尾元素的下一个位置(也可以直接指向队尾元素,根据题意具体情况具体分析)。
存储类型
//顺序队列的存储类型
#define MaxSize 50
typedef struct{
ElemType data[MaxSize];
int front,reat;
}SqQueue;
基本操作
- 初始状态(队空状态): Q.front==Q.rear==0
- 进队操作:队不满时,先送值到队尾元素,再将队尾指针加1
- 出队操作:队不空时,先取队头元素值,再将队头指针加1
循序队列
将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。当队首指针Q.front=MaxSize-1后,再前进一个位置就自动为0,这可以利用除法取余运算(%)来实现。
存储类型
//循环队列的存储类型
#define MaxSize 50
typedef struct{
ElemType data[MaxSize];
int front,reat;
}SqQueue;
基本操作
- 初始化:Q.front=Q.rear=0
//队列的初始化
void InitQueue(SqQueue &Q){
Q.rear=Q.front=0;//初始化队尾与队首指针
}
- 判空: Q.rear==Q.front
//判断队空
bool isEmpty(SqQueue Q){
if(Q.rear==Q.front)return true;//队空的条件
else return false;//队非空
}
- 出队:Q.front=(Q.front+1)%MaxSize
//出队
bool DeQueue(SqQueue &Q,int &x){
//如果队空,则出队失败
if(Q.rear==Q.front){
return false;
}
x=Q.data[Q.front];
Q.front=(Q.front+1)%MaxSize;
return true;
}
- 入队:Q.rear=(Q.rear+1)%MaxSize
//入队
bool EnQueue(SqQueue &Q,int x){
//如果队满,则入队失败
if((Q.rear+1)%MaxSize==Q.front){
return false;
}
Q.data[Q.rear]=x;
Q.rear=(Q.rear+1)%MaxSize;
return true;
}
- 队列长度:(Q.rear+MaxSize-Q.front)%MaxSize
//求队列的长度
int getLength(SqQueue Q){
//如果队空,则返回0
if(Q.rear==Q.front){
return 0;
}
return (Q.rear+MaxSize-Q.front)%MaxSize;//返回队列长度
}
出队入队时,指针都是按照顺时针方向进1,如下图所示:
循环队列判空与判满的三种解决方案
如上图所示,队空的条件是Q.front==Q.rear,但是我们发现一个问题:当入队的速度快于出队的速度时,则队尾指针很快就会追上队头指针,如上图d1所示,此时队满的时候也有Q.front==Q.rear这一条件,这样就无法真正区分循环队列队空还是队满,为此有以下三个解决方案:
方法一:牺牲一个存储单元
牺牲一个单元来区分队空和队满,即在入队时少用一个队列单元,约定以“队头指针再队尾指针的下一个位置作为队满的标志”,如上图d2所示。
采用这种方式后:
- 队满条件: (Q.rear+1)% MaxSize==Q.front
- 队空条件: Q.rear==Q.front
- 队列元素个数: (Q.rear-Q.front+MaxSize)%MaxSize
方法二:类型增设记录型变量size
在队列的存储类型中新增加一个表示元素个数的成员变量size,这样队空的条件为Q.size==0;队满的条件为Q.size=MaxSize
方法三:类型增设标志型变量tag
在队列的存储类型中新增tag成员变量,以区分队空和队满。当tag==0时,若因删除而导致Q.rear==Q.front,则为队空;当tag==1时,若因而导致Q.rear==Q.front,则为队满;
队列的链式存储
队列的链式存储称为链队列,简称链队。它是一个同时带有队头指针与队尾指针的单链表。头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个结点。
链队的存储类型
#define MaxSize 50
//链队中的结点元素
typedef struct LinkNode{
int data;
struct LinkNode *next;
}LinkNode;
//链队
typedef struct{
LinkNode *front,*rear;//队列的队头指针与队尾指针
}LinkQueue;
链队的基本操作
初始化
//链队的初始化
void InitQueue(LinkQueue &Q){
Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode));//建立头结点
Q.front->next=NULL;//初始为空
}
判空
//判断队空
bool isEmpty(LinkQueue Q){
if(Q.rear==Q.front)return true;//队空的条件
else return false;//队非空
}
入队
//入队
bool EnQueue(LinkQueue &Q,int x){
//创建新结点
LinkNode *s=(LinkNode*)malloc(sizeof(LinkNode));
s->data=x; s->next=NULL;
//将新结点插入到链尾,类似单链表的尾插法
Q.rear->next=s;
Q.rear=s;
return true;
}
出队
//出队
bool DeQueue(LinkQueue &Q,int &x){
//如果队空,则出队失败
if(Q.rear==Q.front){
return false;
}
LinkNode *p=Q.front->next;//新建一个临时结点存储当前队头元素,即待删除的元素
x=p->data;//将需要删除的元素值存储进x并返回
Q.front->next=p->next;//修改头结点的指针域,使其指向原来链表的第二个结点,达到逻辑上删除队头结点的效果
//若原链队中只有一个结点,则删除后将变空,需要修改队头与队尾指针使二者相等达到队空的条件
if(Q.rear==p){
Q.rear=Q.front;
}
free(p);//释放待删除结点的内存空间,达到物理上真正的删除
return true;
}
双端队列
双端队列是指允许两端都可以进行入队或者出队操作的队列。其元素的逻辑结构仍为线性结构,将队列的两端分别称之为前端和后端,两端都可以进行入队与出队操作。
双端队列常以选择题的形式考察,对代码要求不高,感兴趣的同学请参考下面这篇文章。
C语言实现双端队列https://blog.csdn.net/Quarrie/article/details/104402199?ops_request_misc=&request_id=&biz_id=102&utm_term=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%8F%8C%E7%AB%AF%E9%98%9F%E5%88%97C%E8%AF%AD%E8%A8%80%E5%AE%9E%E7%8E%B0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-2-104402199.142^v84^koosearch_v1,239^v2^insert_chatgpt&spm=1018.2226.3001.4187
总结
- 队列具有先进先出特性,结合生活中的排队现象加以理解。注意与栈的后进先出特性进行区分
- 栈和队列都是操作受限的线性表,栈只允许在栈顶增加与删除元素,即在表的一端进行操作;队列只允许在队尾加入元素在队头删除元素,即在表的两端进行操作;所以,并不是所有对线性表的操作都能够作用于栈和队列,如随便读取栈或队列中间的某个元素,这是无法实现的
- 顺序队列中注意队尾指针Q.rear指向的是队尾元素的下一个元素还是当前队尾元素,注意审题
- 熟悉循环队列中判满条件,判空条件,求队列元素个数,以及出队时队头指针与入队时队尾指针如何变化,这是常考点也是重点内容
- 熟悉循环队列中区分队空和队满条件的三大解决方案,以及每种方案下判断循环队列队空与队满的条件。
- 法一:牺牲一个存储单元
- 法二,类型中新增表示元素个数的成员变量size
- 法三,类型中新增标志变量tag
- 掌握双端队列中,如何根据已知的出队序列判断是否能够由双端队列入队实现,如果能,能够写出合理的双端队列入队序列,重点
END.