DS 0901(第三章 队列)

本文介绍了队列的基本概念、特点和常见操作,包括初始化、入队、出队、判断队空队满的方法。重点讲解了顺序存储方式实现的循环队列和链式结构的队列,以及双端队列的概念。通过实例分析了不同情况下的队列操作,如队列满的判断和双端队列的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

队列 Queue

线性表:线性表是具有相同数据类型的n个数据元素的有限序列,其中n为表长。当n=0时,线性表是一个空表

栈:是只允许在一端进行插入和删除的线性表

队列:是只允许在一端进行插入,在另一端进行删除的线性表(入队,出队)

terms:

  • 队头:允许删除元素的一端
  • 队尾:允许插入元素的一端
  • 空队列

特点:先进入队列的先出队

常见的基本操作
InitQueue(&Q):初始化队列,创建一个空队列Q。
DestoryQueue(&Q):销毁队列。销毁并释放队列Q所占用的内存空间。

EnQueue:入队。若队列Q没满,将x加入,使之成为新的队尾。
DeQueue:出队。若队列Q非空,删除队头元素,并用x返回。

GetHead(Q,&x):读队头元素,若队列Q非空,则将队头元素赋值给x。

QueueEmpty(Q):判断队空,若队列为空返回true,否则返回false。

用顺序存储的方式实现队列

#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];   //用静态数组存放队列元素 
	int front,rear;			  //队头指针和队尾指针	
}SqQueue; 

void testQueue() {
	SqQueue Q;   //声明一个队列,顺序存储
	//后续操作 
}

队头指针:指向队头元素
队尾指针:指向队尾元素的下一个位置(下一个应该插入的位置)

初始化

//初始化队列 
void InitQueue(SqQueue &Q){
	Q.rear=Q.front=0;
} 

//判断队列是否为空 
bool QueueEmpty(SqQueue Q){
	if(Q.rear==Q.front)		//队空条件 
	return true;
	else
	return false;
} 

入队操作(从队尾方向)

//入队操作
bool EnQueue(SqQueue &Q,ElemType x){
	if(队列已满)				**//见下**
	return false;		 //队满则报错 
	Q.data[Q.rear]=x;    //将x插入队尾
	Q.rear=Q.rear+1;     //将队尾指针后移 
	return true;
} 

当rear队尾指针等于maxsize时,并不能确定队列已经存满,因为从front队头指针出队时,队尾指针的下标是不变的

所以Q.rear=(Q.rear+1)%MaxSize; //将队尾指针后移

  • 模运算可以将无限的整数域映射到有限的整数集合
  • 用模运算将储存空间逻辑上变为了环状
  • 用这种方式形成的队列叫做循环队列

在这里插入图片描述

  • 队列已满的条件是队尾指针的下一个元素是队头指针
  • (Q.rear+1)%MaxSize==Q.front
  • 代价要牺牲掉一个存储单元,防止已满和为空的判断条件相同

出队操作

//出队操作
 bool DeQueue(SeQueue &Q,ElemType &x){
 	if(Q.rear==Q.front)
 	return false;      //队空报错
	 x=Q.data[Q.front];
	 Q.front=(Q.front+1)%MaxSize;
	 return true; 
 } 

 //查,获得队头元素的值,用x返回
 bool GetHead(SeQueue Q,ElemType &x){
 	if(Q.rear==Q.front)
 	return false;    //队空则报错
	 x=Q.data[Q.front];
	 return true; 
 } 

方案一:判断队列已满已空 //浪费一片储存空间

  1. 队满的条件:队尾指针的下一个元素是队头指针,即(Q.rear+1)%MaxSize==Q.front
  2. 队空的条件:Q.rear==Q.front
  3. 队列的元素个数:(rear+maxSize-front)%MaxSize 数论

方案二:判断队列已空已满 初始化时设置一个size变量记录队列当前长度

 #define MaxSize 10
 typedef struct{
 	ElemType data[MaxSize];
 	int front ,rear;
 	int size;      //队列当前长度 
 }SeQueue;
  • 初始化时rear=front=0; size=0;
  • 插入成功size++,删除成功size–
  • 队满条件:size=MaxSize;

方案三:判断队列已空已满 ==使用tag变量来判断最近一次进行的是删除/插入

 #define MaxSize 10
 typedef struct{
 	ElemType data[MaxSize];
 	int front ,rear;
 	int tag;      //队列当前长度 
 }SeQueue;
  • 每次删除操作成功时 都令tag=0;
  • 每次插入操作成功时 都令tag=1;
  • 队满条件front==rear&&tag==1
    队空条件front==rear&&tag==0

注意题目给出的队尾指针所指向的位置

队列的链式实现

类比单链表

 typedef struct LinkNode{			//链式队列结点 
 	ElemType data;
	 struct LinkNode *next; 
 }LinkNode; 
 
 typedef struct{					//链式队列 
 	LinkNode *front,*rear;			
 }LinkQueue;
 

初始化

//出队操作
 bool DeQueue(SeQueue &Q,ElemType &x){
 	if(Q.rear==Q.front)
 	return false;      //队空报错
	 x=Q.data[Q.front];
	 Q.front=(Q.front+1)%MaxSize;
	 return true; 
 } 
 
 //查,获得队头元素的值,用x返回
 bool GetHead(SeQueue Q,ElemType &x){
 	if(Q.rear==Q.front)
 	return false;    //队空则报错
	 x=Q.data[Q.front];
	 return true; 
 } 
 
 #define MaxSize 10
 typedef struct{
 	ElemType data[MaxSize];
 	int front ,rear;
 	int size;      //队列当前长度 
 }SeQueue;
 
 
 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;
 } 
 

判断带头结点的队列是否为空

  • 头指针front和尾指针rear都指向头节点即Q.front==Q.rear
  • 或者是Q.front->next=NULL;

判断不带头结点的头指针是否为空

  • 初始化时让头结点和尾节点都指向NULL
void InitQueue(LinkQueue &Q){
	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)
 	return false;			//空队
	LinkNode *p=Q.front;    //p指向此次出队的结点 
	x=p->data;				//用变量x返回队头元素
	if(Q.rear==p){			//此次是最后一个结点出队 
		Q.front=NULL;
		Q.rar=NULL;
	} 
	free(p);				//释放结点空间 
	return true; 
 } 

队列满的条件

  • 顺序存储:预分配的空间耗尽时队满
  • 链式存储:一般不会队满,除非空间不足

双端队列

def:一种操作受限的线性表

栈:只允许从一端插入和删除的线性表
队列:可以从一段插入,从另一端删除的线性表

**双端队列:**允许从两端插入和两端删除的线性表
引申:

  • 输入受限的双端队列:只允许一端插入,两端删除的双端队列
  • 输出受限的双端队列:只允许一端删除,两端插入的双端队列

考点:判断输出序列的合法性
若元素的输入序列是1,2,3,4,则哪些输出序列是合法的?哪些是非法的?

A44=4!=24

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值