第四章 队列Queue
4.1队列的定义(逻辑结构)
队列Queue是一种操作受限的线性表。只允许在一端进行插入,在另一端删除的线性表。对头是允许删除的一端,称为对头元素。队尾是允许插入的一端,称为队尾元素。队列的特点是先进先出(FIFO)。
4.2队列的基本操作
lnitQueue(&Q):初始化队列。构造一个空队列Q。
DestroyQueue(&Q):销毁队列。销毁并释放队列Q所占用的内存空间。
EnQueue(&Q,x):入队。若队列Q未满,将x加入,使之成为新的队尾。
DeQueue(&Q,&8:出队。若队列Q非空,删除队头元素,并用x返回。
GetHead(Q,&x):读队头元素。若队列Q非空,则将队头元素赋值给x。
QueueEmpty(Q):判队列空。若队列Q为空返回true,否则返回false。
4.3队列的存储结构(物理结构)
4.3.1顺序队列SqQueue
#define MaxSize 10//定义队列中元素的最大个数
typedef struct{
ElemType data [MaxSize] ;//用静态数组字放队列元素
int front, rear;//队头指针和队尾指针
}SqQueue;//定义顺序队列
//初始化队列
void InitQueue(SqQueue &Q){
Q.rear=Q.front=0;//初始时队头、队尾指针指向0
}
//判空操作。判断队列是否为空
bool QueueEmpty(SqQueue Q){
if(Q.rear==Q.front) //队空条件
return true;
else
return false;
}
//入队
bool EnQueue(SqQueue &Q,ElemType x){
if((Q.rear+1)%MaxSize==Q.front)//判满操作,牺牲一个存储单元
return false;//队满则报错
Q.data[Q.rear]=x;//将x插入队尾
Q.rear=(Q.rear+1)%MaxSize;//队尾指针加1取模,存储空间上循环队列
return true;
}
//出队(删除一个队头元素,并用x返回)
bool DeQueue(SqQueue &Q,ElemType &x){
if(Q.rear==Q.front)//判断对空操作 ,牺牲一个存储单元
//或者使用增加size变量的方法记录队列长度
//或者使用增加tag = 0/1的方法用于标记最近的一次操作是 出队或入队
return false;//队空则报错
x=Q.data[Q.front];
Q.front=(Q.front+1)%MaxSize;//队头指针后移
return true;
}
//获得队头元素的值,用x返回
bool GetHead(SqQueue Q,ElemType &x){
if(Q.rear==Q.front)
return false;//队空则报错
x=Q.data[Q.front];
return true;
}
void testQueue(){
SqQueue();//定义一个顺序队列
InitQueue(Q) ; //初始化顺序队列
//...其他代码...
}
4.3.2链式队列LinkQueue
typedef struct LinkNode{//定义链式队列结点
ElemType data;
struct LinkNode *next;
}L inkNode;
typedef struct{//链式队列
LinkNode *front,*rear; //队列的队头和队尾指针
}LinkQueue;
//1初始化队列(带头结点)
void InitQueue(LinkQueue &Q){//初始时front. rear都指向头结点
Q. front=Q. rear=(LinkNode* )malloc(sizeof(LinkNode));
Q. front->next=NULL;
}
//2判断队列是否为空(带头结点)
bool IsEmpty(LinkQueue Q){
if(Q. front==Q. rear)
return true;
else
return false;
}
//3新元素入队(带头结点)
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;//修改表尾rear指针
}
//4对头元素出队(带头结点)
bool DeQueue(LinkQueue &Q, ElemType &x){
if(Q. front==Q,rear)
returnCfalse;//空队
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;
}
//1初始化队列(不带头结点)
void InitQueue( LinkQueue &Q){//初始时front、 rear都指向NULL
Q. front=NULL;
Q. rear=NULL; .
}
//2判断队列是否为空(不带头结点)
bool IsEmpty(LinkQueue Q){
if(Q. front==NULL)
return true;
else
return false;
}
//3新元素入队(不带头结点)
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指针
}
}
//4队头元素出队(不带头结点)
bool DeQueue (LinkQueue &Q, ElemType &x){
if(Q. front==NULL)
return 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;
}
void testLinkQueue(){
LinkQueue();//声明一个链式队列
InitQueue(Q) ; //初始化队列
}
4.3.2.1链式队列(带头结点)
4.3.2.2链式队列(不带头结点)
顺序存储的预分配的空间耗尽时队满,而链式存储一般不会队满 ,除非内存空间不足。
4.3.2.3双端队列
- 双端队列是只允许两端插入、两端删除的线性表。
- 输入受限的双端队列有两类,一种是只允许从一端插入两端删除的线性表,另一种是只允许从一端删除两端插入的线性表。
- 考点:对输出序列的合法性的判断
4.4队列的应用
Eg1:树的层次遍历
Eg2:图的广度优先遍历
Eg3:在操作系统中的应用,使用先来先服务(FCFS) 策略抢用有限系统资源 ,如打印数据缓冲区。