目录
一、队列的基本概念
队列简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队,删除元素称为出队或离队,其操作特性是先进先出。
队头:允许删除的一端
队尾:允许插入的一端
空队列:不含任何元素的空表
二、队列的顺序实现
1、队列的顺序存储
队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:队头指针front指向队头元素,队尾指针rear指向队尾元素的下一个位置(不同的指针定义操作会有所不同),队列的顺序存储类型可以描述为
#define MaxSize 50 //定义队列中元素的最大个数
typedef struct{
ElemType data[MaxSize]; //用数组存放队列元素
int front,rear; //队头指针和队尾指针
}SqQueue;
2、队列的初始化
对队列进行初始化时,队头队尾指针均指向0,判断队列是否为空的条件是队头队尾指针指向相同位置。
3、入队操作
队不满时,先送值到队尾元素,再将队尾指针加1,但能否用Q.rear==MaxSize作为队列满的条件呢?显然不能,当队列元素占满空间,此时进行出队操作,如下图所示,队列中变为7个元素,仍满足Q.rear==MaxSize。这时入队出现“上溢出”,但这种溢出不是真正的溢出,在data数组中依然存在可以存放元素的空位置,所以是一种“假溢出”。
循环队列
对于“假溢出”的问题,这里引入循环队列的概念,将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。
4、出队操作
特定条件下循环队列队头/队尾指针的初值
初始时:Q.front=Q.rear=0
队首指针进1:Q.front=(Q.front+1)%MaxSize
队尾指针进1:Q.rear=(Q.rear+1)%MaxSize
队列长度:(Q.rear+MaxSize-Q.front)%MaxSize
出队入队时:指针都按顺时针方向进1
循环队列队空、队满的判断条件
1)牺牲一个单元来区分队空和队满,入队时少用一个队列单元,约定以“队头指针在队尾指针的下一个位置作为队满的标志”。
队满条件:(Q.rear+1)%MaxSize==Q.front
队空条件:Q.front==Q.rear
队列中元素的个数:(Q.rear-Q.front+MaxSize)%MaxSize
2)类型中增加size数据成员,表示元素个数。删除成功size-1,插入成功size+1。队空时Q.size==0;队满时Q.size==MaxSize,两种情况都有Q.front==Q.rear。
3)类型中增加tag数据成员,以区分队满还是队空。删除成功置tag=0,若导致Q.front==Q.rear,则为队空;插入成功置tag=1,若导致Q.front==Q.rear,则为队满。
5、其他方法
如果队尾指针指向的不是队尾元素的下一个位置,而是队尾元素,则需要先将队尾指针加1,然后再入队。
初始化设置rear=MaxSize-1,入队时先使rear+1,然后将元素插入到rear=0的位置上,判空的条件为(Q.rear+1)%MaxSize==Q.front,而判满的条件有多种选择,方案一是牺牲一个存储单元,此时(Q.rear+2)%MaxSize==Q.front;方案二则是设置size或tag变量进行判断。
三、队列的链式存储
1、队列的链式存储
队列的链式表示称为链队列,它实际是一个同时有队头指针和队尾指针的单链表,头指针指向头节点,尾指针指向队尾结点。
2、链队列的初始化
对于带头结点的链队列,首先申请一个头结点并让front和rear同时指向头结点,然后使头结点的next指针指向NULL。通过front指针和rear指针是否指向同一个结点判断队列是否为空,也可以通过头结点的next指向是否为空即Q.front->next=NULL来判断队列是否为空。
对于不带头结点的链队列,只需让头指针和尾指针都指向NULL即可,判断队列是否为空也通过头指针或尾指针是否指向NULL来判断。
3、入队操作
4、出队操作
不难看出,不带头结点的链式队列在操作中往往比较麻烦,因此通常将链式队列设计成一个带头结点的单链表。链式存储一般不会队满,除非内存不足。
四、双端队列
双端队列是指允许两端都可以进行插入和删除操作的线性表,两端地位是平等的,为了方便理解,将左端也视为前端,右端也视为后端。