数据结构(二)线性结构之队列
队列(Queue
)是具有一定操作约束的线性表。与堆栈有点类似,但是插入和删除操作:只能在一端插入,而在另一端删除。由于是单项链表,所以末端无法进行删除操作。主要操作有两种:
- 数据插入:入队列(
AddQ
) - 数据删除:出队列(
DeleteQ
)
队列满足先来先服务,先进先出:FIFO
。
抽象数据类型描述
队列的抽象数据类型描述如下所示:
类型名称:队列(Queue
)
数据对象集:一个有0个或多个元素的有穷线性表。
操作集:长度为MaxSize
的队列,队列元素;
1、Queue CreatQueue( int MaxSize )
:生成长度为MaxSize
的空队列;
2、int IsFullQ( Queue Q, int MaxSize )
:判断队列Q
是否已满;
3、void AddQ( Queue Q, ElementType item )
:将数据元素item
插入队列Q
中;
4、int IsEmptyQ( Queue Q)
:判断队列Q
是否为空;
5、ElementType DeleteQ( Queue Q)
:将队头数据元素从队列中删除并返回。
队列的顺序存储结构通常由一个一维数组和一个记录队列头元素位置的变量front
以及一个记录队列尾元素位置的变量rear
组成。
#define MaxSize <储存数据元素的最大个数>
typedef struct {
ElementType Data[MaxSize];
int rear; ;
int front;
} Queue;
一个工作队列入一个列表,如下图所示:
添加的时候从右边往里面添加,出队的时候从左边取出。但是这种方式容易造成内存的浪费,比如Job 1
取出来之后,还有Job
想加入就无法从右边加入,会发生冲突。因此有了顺环队列:
那这种方案,堆栈空和满的判别条件是什么?
其实就是Tail
指针和Head
指针指向同一处。而这种指向同一处的情况当空和满是一样的。对于这种情况可以设置Size记录数据大小加以区分;或者判断tag是0还是1来判断最后一次是否是删除操作来判断空、满;或者直接就设置个数组,使得整个队列不会被放满。
程序描述
入出队
void AddQ( Queue *PtrQ, ElementType item)
{
if ( (PtrQ->rear+1) % MaxSize == PtrQ->front ) {
printf(“满");
return
PtrQ->rear = (PtrQ->rear+1 )% MaxSize;
PtrQ->Data[PtrQ->rear] = item;
}
上述的Front
和rearl
指针的移动采用“加1取余”法,体现了顺序存储的“循环使用”。
ElementType DeleteQ ( Queue *PtrQ )
{
if ( PtrQ->front == PtrQ->rear) {
printf(“队列空");
return ERROR;
} else {
PtrQ->front = (PtrQ->front+1)% MaxSize;
return PtrQ->Data[PtrQ->front];
}
}
链式存储
队列的链式存储结构也可以用一个单链表
实现。插入和删除操作分别在链表的两头进行;队列指针front
和rear
应该分别指向链表的哪一头?
typedef struct Node{
ElementType Data;
struct Node *Next;
}QNode;
typedef struct { /*链队列结构*/
QNode *rear; /*指向队尾结点*/
QNode *front; /* 指向队头结点*/
} LinkQueue;
LinkQueue *PtrQ ;
ElementType DeleteQ ( LinkQueue *PtrQ )
{ Qnode *FrontCell;
ElementType FrontElem;
if ( PtrQ->front = NULL) {
printf(“队列空"); return ERROR;
}
FrontCell = PtrQ->front;
if( PtrQ->front == PtrQ->rear) /* 若队列只有一个元素*/
PtrQ->front = PtrQ->rear = NULL; /* 删除后队列置为空*/
else
PtrQ->front = PtrQ->front->Next;
FrontElem = FrontCell->Data;
free( FrontCell ); /* 释放被删除结点空间*/
return FrontElem;