文章目录
队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列的定义
队列(queue),和栈一样,也是一种对数据的“存”和“取”有严格要求的线性存储结构。是一种先进先出的线性表。在队列中,允许插入的一端称为队尾(rear),允许删除的一端称为对头(front)。
队的存储示意图:
与栈结构不同的是,队列的两端都"开口",要求数据只能从一端进,从另一端出
通常,称进数据的一端为 “队尾”,出数据的一端为 “队头”,数据元素进队列的过程称为 “入队”,出队列的过程称为 “出队”。
队列的抽象数据类型定义:
ADT Queue{
数据对象:D={ai | ai∈ElemSet,i=1,2,…,n,n≥0}
数据关系:R={< a(i-1) , ai > | a(i-1), ai ∈ D,i=2,…,n}
基本操作:
InitQueue(&Q)
// 构造一个空队列Q
DestroyQueue(&Q)
// 销毁队列Q
ClearQueue(&Q)
// 清空队列Q
QueueEmpty(Q)
// 若Q为空队列,返回true,否则返回false
QueueLength(Q)
// 返回Q的元素个数,即队列的长度
GetHead(Q)
// 返回Q的队头元素
EnQueue(&Q,e)
// 插入元素e为Q的新的队尾元素
DeQueue(&Q,&e)
// 删Queue除Q的队头元素,并用e返回其值
QueueTraverse(Q)
// 从队头到队尾,依次对Q的每个元素访问
}ADT Queue
队列的常见应用
- 脱机打印输出:按申请的先后顺序依次输出
- 多用户系统中,多个用户排成队,分时地循环使用CPU和主存
- 按用户地优先级排成多个队,每个优先级一个队列
- 实时控制系统中,信号按接受地先后顺序依次处理
- 网络电文传输,按到达的时间顺序依次进行
循环队列
顺序队列:在顺序表的基础上实现的队列结构。
循环队列的顺序表示和实现
//---------队列的顺序存储结构--------
#define MAXQSIZE 100 //最大队列长度
typedef struct{
QElemType *base; //初始化的动态分配存储空间
int front; //头指针,若队列不空,指向队列头元素
int rear; //尾指针 ,若队列不空,指向队列尾元素的下一个位置
}SqQueue;
为了在c语言中描述方便起见,在此约定:初始化创建空队列时,令front=rear=0,每当插入新的队列尾元素时,尾指针rear增1;每当删除队列头元素时,头指针front增1。因此,在非空队列中,头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置,如图:
假设当前队列分配的最大空间为6,则当队列处于图4所示的状态时不可再继续擦插入新的队尾元素,否则会出现溢出现象,即因数组越界而导致程序的非法操作错误。事实上此时的队列实际可用空间并未占满,所以称为这种现象为“假溢出”现象。这是由“队尾入队,队头出队”这种受限制的操作造成的。
解决假上溢的方法
1.将队列元素依次向队头方向移动
缺点:浪费时间。没移动一次,队中元素都要移动一次。
2.将队空间设想成一个循环的表,即分配给队列的m个存储单元可以循环使用,当rear为maxqsise时,若向量的开始端空着,又可从头使用空着的空间。当front为maxqsize时,也是一样。
这就是引入循环队列,将其队列想象成环状的空间。
头、尾指针以及队列元素之间的关系不变,只是在循环队列中,头、尾指针“依环状增1”的操作可用“模”运算来实现。通过取模【Q.rear = (Q.rear + 1)%MAXQSIZE】,头指针和尾指针就可以在顺序表空间内以头尾衔接的方式“循环”移动。
可是这样操作后会存在这样一个情况,如图:
由图(d)可知道若出现图(d1)情况,rear = front,导致无法判断队满还是队空;
由此