[考研数据结构] 第3章之队列的基本知识与操作

文章目录

队列的基本概念

队列的顺序存储

顺序队列

 存储类型

基本操作

循序队列

 存储类型

基本操作

循环队列判空与判满的三种解决方案  

方法一:牺牲一个存储单元

方法二:类型增设记录型变量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语言实现双端队列icon-default.png?t=N3I4https://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.

猜你喜欢

转载自blog.csdn.net/qq_52487066/article/details/130233228