队列 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;
}
方案一:判断队列已满已空 //浪费一片储存空间
- 队满的条件:队尾指针的下一个元素是队头指针,即
(Q.rear+1)%MaxSize==Q.front
- 队空的条件:Q.rear==Q.front
- 队列的元素个数:(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