目录
本文思维导图:
1、初始化
定义队头和队尾指针是为了便于入队和出队:
#include<stdio.h>
#define MaxSize 10 //定义队列中元素的最大个数
typedef struct {
int data[MaxSize]; //用静态数组存放队列元素
int front, rear; //队头指针和队尾指针
}SqQueue;
刚开始需要初始化队头、队尾指针指向0:
void InitQueue(SqQueue& Q) {
Q.rear = 0, Q.front = 0; //初始队头队尾指针均指向0
}
此时队列在内存中的存储情况如下↓
队头指针和队尾指针指向的位置:(一般情况下是这样,当然考试时题目可能会自己规定指向,所以考试时需要审题那些队头队尾指针究竟指向什么位置):
队头指针:总是指向队头元素。
队尾指针:总是指向队尾元素的后一个位置(下一个应该插入的位置)
比如在上述声明的队列中依次加入abcde五个元素,此时指针位置如下↓
2、判空、判满
判断队列是否为空,只需判断队头指针和队尾指针是否相等,因为初始化队列时,两个指针指向的同一个位置,而只要队列不为空,那么两个指针一定是一前一后的:
//判断队列是否为空
bool QueueEmpty(SqQueue Q) {
if (Q.rear == Q.front) return true;
else return false;
}
判断队列是否已满的条件是:队尾指针的再下一个位置是否是队头,即 (Q.rear + 1) % MaxSize == Q.front ,下面进行详细解释:
队列的存储模式可以近似看成一个循环结构:
前面说过,当队头队尾指针指向相同位置时,队列为空,所以为了避免与该条件起冲突,我们需要牺牲掉一个存储单元(如上图中的2处),让判断队列已满的条件是:队尾指针的再下一个位置是队头,这样就可以避免冲突。
//判断队列是否已满
bool QueueMaxsize(SqQueue Q) {
if ((Q.rear + 1) % MaxSize == Q.front) return true;
else return false;
}
当然,如果不想牺牲掉一个存储单元也是可行的,其中有一个方法是:将队列结构体中多定义一个size变量来记录当前队列的长度,初始时size大小是0,当入队成功,size自增1;出队成功,size自减1。此时判断队满的条件就是 size==MaxSize。
3、入队
先看下代码↓
//入队
bool EnQueue(SqQueue& Q, int x) {
if ((Q.rear + 1) % MaxSize == Q.front) //如果队列已满
return false; //队满则报错
Q.data[Q.rear] = x; //新元素插入队尾
Q.rear = (Q.rear + 1) % MaxSize; //队尾指针加1取模
return true;
}
队尾指针加1取模,是因为当队列后半部分存储满了之后,如果队列前半部分还有空余,那么需要通过加一并取模的方法,让rear指针到达队头位置,进而进行入队操作。
4、出队
//出队
bool DeQueue(SqQueue& Q, int& x) {
if (Q.rear == Q.front) return false; //队空则报错
x = Q.data[Q.front];
Q.front = (Q.front + 1) % MaxSize;
return true;
}
5、查看队头元素
//获得队头元素的值,用x返回
bool GetHead(SqQueue Q, int& x) {
if (Q.rear == Q.front) return false; //队空则报错
x = Q.data[Q.front];
return true;
}
6、补充知识点
1、队列元素个数公式:(rear+MaxSize-front) % MaxSize
2、考试的时候可能会出现其它规定,比如改变首尾指针初始的朝向等。不过万变不离其宗,只要掌握取余算法这一核心思想,就能解决掉大部分问题。