文章目录
队列
考纲内容
复习提示
1.队列的基本概念
1.1队列的定义
1.2队列常见的基本操作
1.3重要考点
2.队列的顺序存储结构
2.1队列的顺序存储
2.2循环队列
2.3循环队列的操作
2.4重要考点
3.队列的链式存储结构
3.1队列的链式存储
3.2链式队列的基本操作
3.3重要考点
4.双端队列
4.1双端队列定义
4.2重要考点
队列
考纲内容
(一)栈和队列的基本概念
(二)栈和队列的顺序存储结构
(三)栈和队列的链式存储结构
(四)多维数组的存储(五)特殊矩阵的压缩存储
(六)栈、队列和数组的应用
复习提示
本章通常以选择题的形式考查,题目不算难,但命题的形式比较灵活,其中栈(出入栈的过程、出栈序列的合法性)和队列的操作及其特征是重点。因为它们均是线性表的应用和推广,所以也容易出现在算法设计题中。此外,栈和队列的顺序存储、链式存储及其特点、双端队列的特点、栈和队列的常见应用,以及数组和特殊矩阵的压缩存储都是必须掌握的内容。
1.队列的基本概念
1.1队列的定义
队列(Queue)简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。这和我们日常生活中的排队是一致的,最早排队的也是最早离队的,其操作的特性是先进先出。
队头(Front)。允许删除的一端,又称队首。
队尾(Rear)。允许插入的一端。
空队列。不含任何元素的空表。
1.2队列常见的基本操作
- InitQueue(&Q):初始化队列,构造一个空队列 Q。
- QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回 false。
- EnQueue(&Q,x):入队,若队列Q未满,将x加入,使之成为新的队尾
- DeQueue(&Q,&x):出队,若队列Q非空,删除队头元素,并用x返回。
- GetHead(Q,&x):读队头元素,若队列Q非空,则将队头元素赋值给x。
需要注意的是,栈和队列是操作受限的线性表,因此不是任何对线性表的操作都可以作为栈和队列的操作。比如,不可以随便读取栈或队列中间的某个数据。
1.3重要考点
2.队列的顺序存储结构
2.1队列的顺序存储
队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:队头指针 front 指向队头元素,队尾指针rear 指向队尾元素的下一个位置(不同教材对front和rear的定义可能不同,例如,可以让 rear 指向队尾元素、front 指向队头元素。对于不同的定义,出队入队的操作是不同的)。
队列的顺序存储类型可描述为:
#define MaxSize 50 //定义队列中元素的最大个数
typedef struct{
ElemType data[MaxSize]; //用数组存放队列元素
int front,rear; //队头指针和队尾指针
} SqQueue;
初始时:Q.front=Q.rear=0
进队操作:队不满时,先送值到队尾元素,再将队尾指针加1。
出队操作:队不空时,先取队头元素值,再将队头指针加1。
图 3.6(a)所示为队列的初始状态,有 Q.front==Q.rear==0成立,该条件可以作为队列判空的条件。但能否用 Q.rear==Maxsize作为队列满的条件呢?显然不能,图 3.6(d)中,队列中仅有一个元素,但仍满足该条件。这时入队出现“上溢出”,但这种溢出并不是真正的溢出,在data 数组中依然存在可以存放元素的空位置,所以是一种“假溢出”。
2.2循环队列
上面指出了顺序队列“假溢出”的问题,这里引出循环队列的概念。将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。当队首指针 Q.front=MaxSize-1后,再前进一个位置就自动到0,这可以利用除法取余运算(%)来实现。
- 初始时: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(如图3.7所示)。
那么,循环队列队空和队满的判断条件是什么呢?显然,队空的条件是Q.front==Q.rear。若入队元素的速度快于出队元素的速度,则队尾指针很快就会赶上队首指针,如图 3.7(d1)所示,此时可以看出队满时也有Q.front==Q.rear。循环队列出入队示意图如图 3.7所示。
为了区分是队空还是队满的情况,有三种处理方式:
1) 牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是一种较为普遍的做法,约定以“队头指针在队尾指针的下一位置作为队满的标志”,如图 3.7(d2)所示。
- 队满条件:(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,则为队满。
2.3循环队列的操作
(1)初始化
void InitQueue(SqQueue &Q){ //初始化队首、队尾指针
Q.rear=Q.front=0;
}
(2)判队空
bool isEmpty(SqQueue Q){
if(Q.rear==Q.front) //队空条件
return true;
else
return false;
}
(3)入队
bool EnQueue(SqQueue &Q,ElemType x){
if((Q.rear+1)号MaxSize==Q.front) //队满则报错
return false;
Q.data[Q.rear]=x;
Q.rear=(Q.rear+l)%MaxSize; //队尾指针加1取模
return true;
}
(4)出队
bool DeQueue(SqQueue &Q,ElemType &x){
if(Q.rear==Q.front) //队空则报错
return false;
x=Q.data[Q.front];
Q.front=(Q.front+l)%MaxSize; //队头指针加1取模
return true;
}
2.4重要考点
3.队列的链式存储结构
3.1队列的链式存储
队列的链式表示称为链队列,它实际上是一个同时有队头指针和队尾指针的单链表。头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个结点。
队列的链式存储类型可描述为:
typedef struct linkNode( //链式队列结点
ElemType data;
struct linkNode *next;
}LinkNode;
typedef struct{ //链式队列
LinkNode *front,*rear; //队列的队头和队尾指针
}LinkQueue;
不带头结点时,当Q.front==NULL且Q.rear==NULL时,链式队列为空。
入队时,建立一个新结点,将新结点插入到链表的尾部,并让Q.rear 指向这个新插入的结点(若原队列为空队,则令Q.front也指向该结点)。出队时,首先判断队是否为空,若不空,则取出队头元素,将其从链表中摘除,并让Q.front 指向下一个结点(若该结点为最后一个结点,则置Q.front 和Q.rear 都为 NULL)。
不难看出,不带头结点的链式队列在操作上往往比较麻烦,因此通常将链式队列设计成一个带头结点的单链表,这样插入和删除操作就统一了,如图3.9所示。
用单链表表示的链式队列特别适合于数据元素变动比较大的情形,而且不存在队列满且产生溢出的问题。另外,假如程序中要使用多个队列,与多个栈的情形一样,最好使用链式队列,这样就不会出现存储分配不合理和“溢出”的问题。
3.2链式队列的基本操作
(1)初始化
void InitQueue(LinkQueue &Q){ //初始化带头结点的链队列
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; //插入链尾
Q.rear=s; //修改尾指针
(4)出队
bool DeQueue(linkQueue &Q,ElemType &x){
if(Q.front==Q.rear)
return false; //空队
LinkNode *p=Q.front->next;
x=p->data;
Q.front->next=p->next;
if(Q.rear==p)
Q.rear=Q.front; //若原队列中只有一个结点,删除后变空
free(p);
return true;
}
3.3重要考点
4.双端队列
4.1双端队列定义
双端队列是指允许两端都可以进行插入和删除操作的线性表,如图3.10所示。双端队列两端的地位是平等的,为了方便理解,将左端也视为前端,右端也视为后端。
在双端队列进队时,前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列中前端进的元素的后面。在双端队列出队时,无论是前端还是后端出队,先出的元素排列在后出的元素的前面。
输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许插入的双端队列称为输出受限的双端队列。
输入受限的双端队列:允许在一端进行插入和删除,但在另一端只允许删除的双端队列称为输入受限的双端队列。
若限定双端队列从某个端点插入的元素只能从该端点删除,则该双端队列就蜕变为两个栈底相邻接的栈。