数据结构-王道2017-第3章 栈和队列-队列
1.队列简称队,也是一种操作受限的线性表,只允许在表的一端插入,而在表的另一端删除 先进先出FIFO,不能随便读取队列中间的某个数据
队头(Front):允许删除的一端,又称为队首
队尾(rear):允许插入的一端
空队列:不含任何元素的空表
基本操作: InitQueue(&Q),QueueEmpty(Q),EnQueue(&Q,x),DeQueue(&Q,&x),GetHead(Q,&x)
2.队列的顺序存储
设队头指针指向队头元素,队尾指针指向队尾元素的下一个位置(队头指针指向队头元素的钱一个位置,队尾指针指向队尾也可以)
typedef struct { ElemType data[MaxSize]; int front, rear; }SqQueue;
队空条件:Q.front == Q.rear == 0;
但是队满条件不能是Q.rear==MaxSize,因为当front == Q.rear-1时,仍然妈祖条件,但此时队中只有一个元素,此时入队出现“上溢出”,但这种溢出并不是真正的溢出,这是顺序队列的缺点
3.循环队列
当队首指针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; //加上MaxSize的原因是Q.rear可能循环到Q.front的左边,这时长度就是Q.rear+MaxSize - Q.front,%MaxSize的目的是Q.rear在Q.front的右边时,加上MaxSize就不是准确值了,所以取模,而取模对前一种情况是没有影响的
出队入队时:指针都按顺时针方向进1
且队空的条件为Q.front=Q.rear,而队满的条件也是Q.front=Q.rear(队尾指针追上了队首指针),解决方案:
1)牺牲一个单元来区分队空队满,入队时少用一个队列单元,这是一种较为普遍的做法,约定以“队头指针在队尾指针的下一个位置作为队满的标志”。
队满条件:(Q.rear+1)%MaxSize==Q.front
队空条件:Q.front==Q.rear
队列中元素的个数:(Q.rear-Q.front+MaxSize)%MaxSize
2) 类型中增设表示元素个数的数据成员。这样队空的条件为Q.size == 0;队满的条件为Q.size==MaxSize,两种情况都有Q.front==Q.rear
3) 类型中增设tag数据成员,以区分堆满还是队空。tag等于0的情况下,若因删除导致Q.front==Q.rear则为队空,tag等于1的情况下,若因插入导致Q.front==Q.rear则为队满
3.循环队列的操作
//队列的顺序存储结构 const int MaxSize = 50; typedef int ElemType; typedef struct { ElemType data[MaxSize]; int front, rear; }SqQueue; void InitQueue(SqQueue &q) { q.front = q.front = 0; } bool QueueEmpty(SqQueue q) { return q.rear == q.front; } bool EnQueue(SqQueue &q,ElemType x) { if ((q.rear + 1) % MaxSize == q.front) return false; q.data[q.rear] = x; q.rear = (q.rear + 1) % MaxSize; return true; } bool DeQueue(SqQueue &q, ElemType &x) { if (q.front == q.rear) //队列为空 return false; x = q.data[q.front]; q.front = (q.front + 1) % MaxSize; return true; }
4.队列的链式存储结构
typedef struct{ ElemType data; LinkNode *next; }LinkNode; //注意不是指针 typedef struct { LinkNode *front, *rear; }LinkQueue;
队列的判空条件为:Q.front==NULL&&Q.rear==NULL,通常将链式队列设计成一个带头节点的单链表,这样插入和删除操作就统一了,不存在队满且产生溢出的问题
//队列的顺序存储结构 const int MaxSize = 50; typedef int ElemType; typedef struct{ ElemType data; LinkNode *next; }LinkNode; //注意不是指针 typedef struct { LinkNode *front, *rear; }LinkQueue; void InitQueue(LinkQueue &q) { q.rear = q.front = (LinkNode*)malloc(sizeof(LinkNode)); q.front->next = NULL; } bool QueueEmpty(LinkQueue q) { return q.rear == q.front; } void EnQueue(LinkQueue &q,ElemType x) { LinkNode *p = (LinkNode*)malloc(sizeof(LinkNode)); p->data = x; p->next = NULL; q.rear->next = p; q.rear = p; } bool DeQueue(LinkQueue &q, ElemType &x) { if (q.rear == q.front) return false; LinkNode *p = q.front->next; x = p->data; q.front->next = p->next; if (p == q.front) //只有一个元素的时候要置尾指针为空 q.rear = q.front; free(p); return true; }
5.双端队列
双端队列:指允许两段都可以进行入队和出队操作的队列,其元素的逻辑结构仍然是线性结构,两端分别称为前端和后端
输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许插入的双端队列称为输出受限的双端队列
输入受限的双端队列:允许在一端进行插入和删除,但在另一端只允许删除的双端队列称为输入受限的双端队列
如果限定双端队列从某个端点插入的元素只能从该端点删除,则该双端队列就蜕变为两个栈底相邻接的栈了
6.试题精选
1)栈和队列都只能顺序存取
2)进队操作,只有尾指针变化,出队操作除了队中只有一个元素外(也会更改rear指针),只会更改front指针