目录
循环队列原理图:(原文链接:https://blog.csdn.net/u010429311/article/details/51043149)
1.什么是队列?
队列就是一个队伍。数据结构中,队列的特点是先进先出。 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。 和栈一样,队列是一种操作受限制的线性表(插入操作只能从一端操作,这一端叫作队尾;而移除操作也只能从另一端操作,这一端叫作队首)。
队列和栈一样,由一段连续的存储空间组成,是一个具有自身特殊规则的数据结构。我们说栈是后进先出的规则,队列刚好与之相反,是一个先进先出(FIFO,First In First Out)或者说后进后出(LILO,Last In Last Out)的数据结构。
2.队列的两种存储方式
第一种.顺序存储
采用数组来保存队列的元素,设立一个队首指针front,一个队尾指针rear,分别指向队首和队尾元素,则rear-front即为存储的元素个数!
算法实现的过程:
1.宏定义队列的最大容量
#define MaxSize 5 //队列的最大容量
2.类型定义队列中元素类型
typedef int DataTtype;//队列中元素类型
3.用结构体实现队列中数据的存储
typedef struct Queue{
DataType queue[MaxSize];
int front; //队头指针
int rear; //队尾指针
}SeqQueue;
4.队列初始化:把队头和队尾指针同时置0
//队列初始化,将队列初始化为空队列
void InitQueue(SeqQueue* SQ){
if(!SQ)return;
SQ->front = SQ->rear = 0;//把队头和队尾指针同时置0
}
5.判断队列是否为空 判断条件为front==rear
//判断队列为空
int IsEmpty(SeqQueue* SQ) {
if (!SQ)return 0;
if (SQ->rear == SQ->front) {
//cout << "空队列!" << endl;
return 1;
}
return 0;
}
6.判断队列是否为满 判断条件rear==MaxSize
//判断队列为满
int IsFull(SeqQueue* SQ) {
if (!SQ)return 0;
if (SQ->rear == MaxSize) {
//cout << "队列已满!" << endl;
return 1;
}
return 0;
}
7.入队,将元素data插入到队列SQ中,然后rear加1.在进行队列的元素插入时,需要判断队列是否已经满,在队尾插入元素
//入队,将元素data插入到队列SQ中
int EnterQueue(SeqQueue* SQ, DataType data) {
if (!SQ)return 0;
if (IsFull(SQ)) {
cout << "无法插入元素" << data << ",队列已满!" << endl;
return 0;
}
SQ->queue[SQ->rear] = data;//在队尾插入元素data
SQ->rear++;//队尾指针后移一位
return 1;
}
8.打印队列中的各元素 相当于数组元素的打印
小知识点:设置打印元素之间的距离用setw()函数,需要添加头文件#define <iomanip>
C++ setw() 函数用于设置字段的宽度,语法格式如下: setw(n) n 表示宽度,用数字表示。 setw() 函数只对紧接着的输出产生作用。 当后面紧跟着的输出字段长度小于 n 的时候,在该字段前面用空格补齐,当输出字段长度大于 n 时,全部整体输出。
//打印队列中的各元素
void PrintQueue(SeqQueue* SQ) {
if (!SQ)return;
int i = SQ->front;
while (i < SQ->rear) {
cout << setw(4) << SQ->queue[i];
i++;
}
}
9.出队
方式一:将队列中队头的元素data出队,后面的元素向前移动 需要判断队列是否为空
移动元素时用for循环,可以有以下两种写法:
写法一:
for (int i = SQ->front + 1; i < SQ->rear; i++) {
SQ->queue[i - 1] = SQ->queue[i];
}
写法二:
for (int i = SQ->front; i < SQ->rear - 1; i++) {
SQ->queue[i] = SQ->queue[i + 1];
}
元素出队后记得队尾指针前移一位
//出队,将队列中队头的元素data出队,后面的元素向前移动
int DeleteQueue(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
if (!data)return 0;
*data = SQ->queue[SQ->front];
for (int i = SQ->front + 1; i < SQ->rear; i++) {
SQ->queue[i - 1] = SQ->queue[i];
}
SQ->rear--;//队尾指针前移一位
return 1;
}
方式二:将队列中队头的元素data出队,出队后,队头指针front后移一位 需要判断队列是否为空
当队头指针front后移一位,front==MaxSize时,队列已到队尾,无法出队!
//出队,将队列中队头的元素data出队,出队后,队头指针front后移一位
int DeleteQueue2(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
if (SQ->front == MaxSize) {
cout << "队列已到尽头!" << endl;
return 0;
}
*data = SQ->queue[SQ->front];//出队元素值
SQ->front++;//队首指针后移一位
return 1;
}
10.获取队首元素,不出队 需要判断队列是否为空
//获取队首元素,不出队
int GetHead(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
*data = SQ->queue[SQ->front];
return *data;
}
11.清空队列 把队头和队尾指针同时置0
//清空队列
void ClearQueue(SeqQueue* SQ) {
if (!SQ)return;
SQ->front = SQ->rear = 0;
}
12.获取队列中元素个数 rear-front 即为存储的元素个数
//获取队列中的元素个数
int GetCount(SeqQueue* SQ) {
if (!SQ)return 0;
return SQ->rear - SQ->front;
}
完整代码实现:
#include <iostream>
#include <iomanip>
#define MaxSize 5 //队列的最大容量
using namespace std;
typedef int DataType;//队列中元素类型
typedef struct Queue {
DataType queue[MaxSize];
int front;//队头"指针"
int rear; //队尾"指针"
}SeqQueue;
//队列初始化,将队列初始化为空队列
void InitQueue(SeqQueue* SQ) {
if (!SQ)return ;
SQ->front = SQ->rear = 0;//把队头和队尾指针同时置0
}
//判断队列为空
int IsEmpty(SeqQueue* SQ) {
if (!SQ)return 0;
if (SQ->rear == SQ->front) {
//cout << "空队列!" << endl;
return 1;
}
return 0;
}
//判断队列为满
int IsFull(SeqQueue* SQ) {
if (!SQ)return 0;
if (SQ->rear == MaxSize) {
//cout << "队列已满!" << endl;
return 1;
}
return 0;
}
//入队,将元素data插入到队列SQ中
int EnterQueue(SeqQueue* SQ, DataType data) {
if (!SQ)return 0;
if (IsFull(SQ)) {
cout << "无法插入元素" << data << ",队列已满!" << endl;
return 0;
}
SQ->queue[SQ->rear] = data;//在队尾插入元素data
SQ->rear++;//队尾指针后移一位
return 1;
}
//打印队列中的各元素
void PrintQueue(SeqQueue* SQ) {
if (!SQ)return;
int i = SQ->front;
while (i < SQ->rear) {
cout << setw(4) << SQ->queue[i];
i++;
}
}
//出队,将队列中队头的元素data出队,后面的元素向前移动
int DeleteQueue(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
if (!data)return 0;
*data = SQ->queue[SQ->front];
for (int i = SQ->front + 1; i < SQ->rear; i++) {
SQ->queue[i - 1] = SQ->queue[i];
}
SQ->rear--;//队尾指针前移一位
return 1;
}
//出队,将队列中队头的元素data出队,出队后,队头指针front后移一位
int DeleteQueue2(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
if (SQ->front == MaxSize) {
cout << "队列已到尽头!" << endl;
return 0;
}
*data = SQ->queue[SQ->front];//出队元素值
SQ->front++;//队首指针后移一位
return 1;
}
//获取队首元素,不出队
int GetHead(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
*data = SQ->queue[SQ->front];
return *data;
}
//清空队列
void ClearQueue(SeqQueue* SQ) {
if (!SQ)return;
SQ->front = SQ->rear = 0;
}
//获取队列中的元素个数
int GetCount(SeqQueue* SQ) {
if (!SQ)return 0;
return SQ->rear - SQ->front;
}
int main(void) {
SeqQueue* SQ = new SeqQueue;
DataType data;
//1.初始化队列
InitQueue(SQ);
//2.入队
for (int i = 0; i < 7; i++) {
EnterQueue(SQ,i);
}
//打印队列中的元素
cout << "队列中的元素:";
PrintQueue(SQ);
cout << endl;
//出队
DeleteQueue(SQ, &data);
cout << "出队的元素是:" << endl;
cout << "出队一个元素后,剩下的元素:";
PrintQueue(SQ);
cout << endl;
return 0;
}
第二种存储:链式存储
队列的链式存储,其实就是线性表的单链表,只不过它只是尾进头出而已,我们把它简称为链队列。为了操作上的方便,我们将队头指针指向链队列的头节点,而队尾指针指向终端节点。
算法的实现过程:
1.宏定义队列的最大容量
#define MaxSize 5 //队列的最大容量
2.类型定义 设置队列中元素类型
typedef int DataType;//队列中的元素类型
3.单链表节点结构
typedef struct _QNode {
DataType data; //链表的数据域
struct _QNode* next; //链表的指针域
}QNode;
4.类型定义节点指针
typedef QNode* QueuePtr;
5.队列结构
typedef struct Queue {
int length; //队列的长度
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue;
6.队列初始化,将队列初始化为空队列 把队头和队尾指针同时置空
//队列初始化,将队列初始化为空队列
void InitQueue(LinkQueue* LQ) {
if (!LQ)return;
LQ->length = 0;
LQ->front = LQ->rear = NULL;//把队头和队尾指针同时置空
}
7.判断队列是否为空 判断条件就是LQ->front==NULL
//判断队列是否为空
int IsEmpty(LinkQueue* LQ) {
if (!LQ)return 0;
if (LQ->front == NULL) {
return 1;
}
return 0;
}
8.判断队列是否为满 判断条件是LQ->length==MaxSize
//判断队列是否为满
int IsFull(LinkQueue* LQ) {
if (!LQ)return 0;
if (LQ->length == MaxSize) {
return 1;
}
return 0;
}
9.入队,将元素data插入到队列LQ中
需要判断LQ是否为满,定义一个节点指针,new 一个节点,判断是否为空队列,空队列时:队头队尾指针同时指向新节点,不是空队列:将新节点连接到队尾节点的后面,然后队尾指针指向新节点(LQ->rear->next = qNode;LQ->rear = qNode;),最后记得队长(chang)加1(LQ->lengrh++;)
//入队,将元素data插入到队列LQ中
int EnterQueue(LinkQueue* LQ, DataType data) {
if (!LQ)return 0;
if (IsFull(LQ)) {
cout << "无法插入元素" << data << ",队列已满!" << endl;
return 0;
}
QNode* qNode = new QNode;
qNode->data = data;
qNode->next = NULL;
if (IsEmpty(LQ)) {//空队列
LQ->front = LQ->rear = qNode;
}
else {
LQ->rear->next = qNode;//在队尾插入节点qNode
LQ->rear = qNode;
}
LQ->length++;
return 1;
}
10.出队,将队列中队头的元素出队,其后的第一个元素成为新的队首
判断是否为空队列,定义一个临时指针指向LQ->front,用来释放资源,还需要判断如果队头出列后不存在其他元素,则rear节点也要置空,最后LQ->length--;
//出队,将队列中队头的元素出队,其后的第一个元素成为新的队首
int DeleteQueue(LinkQueue* LQ, DataType* data) {
if (!LQ || IsEmpty(LQ)) {
cout << "队列为空!" << endl;
return 0;
}
if (!data)return 0;
QueuePtr tmp = LQ->front;
LQ->front = tmp->next;
if (!LQ->front)LQ->rear = NULL;//如果队头出列后不存在其他元素,则rear节点也要置空
*data = tmp->data;
delete tmp;
LQ->length--;
return 1;
}
11.打印队列中的各元素 使用while循环
//打印队列中的各元素
void PrintQueue(LinkQueue* LQ) {
if (!LQ)return;
if (LQ->front == NULL) {
cout << "队列为空!" << endl;
return;
}
QueuePtr tmp = LQ->front;
while (tmp) {
cout << setw(4) << tmp->data;
tmp = tmp->next;
}
cout << endl;
}
12.获取队首元素,不出队
//获取队首元素,不出队
int getHeadElem(LinkQueue* LQ, DataType* data) {
if (!LQ || IsEmpty(LQ)) {
cout << "队列为空!" << endl;
return 0;
}
if (!data)return 0;
*data = LQ->front->data;
return 1;
}
13. 清空队列
//清空队列
void ClearQueue(LinkQueue* LQ) {
if (!LQ)return;
while (LQ->front) {
QueuePtr tmp = LQ->front->next;
delete LQ->front;
LQ->front = tmp;
}
LQ->front = LQ->rear = NULL;
LQ->length = 0;
}
14.获取队列中元素的个数
//获取队列中元素的个数
int getLength(LinkQueue* LQ) {
if (!LQ)return 0;
return LQ->length;
}
完整代码实现:
#include <iostream>
#include <iomanip>
#define MaxSize 5 //队列的最大容量
using namespace std;
typedef int DataType;//队列中的元素类型
typedef struct _QNode {
DataType data;
struct _QNode* next;
}QNode;
typedef QNode* QueuePtr;
typedef struct Queue {
int length; //队列的长度
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue;
//队列初始化,将队列初始化为空队列
void InitQueue(LinkQueue* LQ) {
if (!LQ)return;
LQ->length = 0;
LQ->front = LQ->rear = NULL;//把队头和队尾指针同时置空
}
//判断队列是否为空
int IsEmpty(LinkQueue* LQ) {
if (!LQ)return 0;
if (LQ->front == NULL) {
return 1;
}
return 0;
}
//判断队列是否为满
int IsFull(LinkQueue* LQ) {
if (!LQ)return 0;
if (LQ->length == MaxSize) {
return 1;
}
return 0;
}
//入队,将元素data插入到队列LQ中
int EnterQueue(LinkQueue* LQ, DataType data) {
if (!LQ)return 0;
if (IsFull(LQ)) {
cout << "无法插入元素" << data << ",队列已满!" << endl;
return 0;
}
QNode* qNode = new QNode;
qNode->data = data;
qNode->next = NULL;
if (IsEmpty(LQ)) {//空队列
LQ->front = LQ->rear = qNode;
}
else {
LQ->rear->next = qNode;//在队尾插入节点qNode
LQ->rear = qNode;
}
LQ->length++;
return 1;
}
//出队,将队列中队头的元素出队,其后的第一个元素成为新的队首
int DeleteQueue(LinkQueue* LQ, DataType* data) {
if (!LQ || IsEmpty(LQ)) {
cout << "队列为空!" << endl;
return 0;
}
if (!data)return 0;
QueuePtr tmp = LQ->front;
LQ->front = tmp->next;
if (!LQ->front)LQ->rear = NULL;//如果队头出列后不存在其他元素,则rear节点也要置空
*data = tmp->data;
delete tmp;
LQ->length--;
return 1;
}
//打印队列中的各元素
void PrintQueue(LinkQueue* LQ) {
if (!LQ)return;
if (LQ->front == NULL) {
cout << "队列为空!" << endl;
return;
}
QueuePtr tmp = LQ->front;
while (tmp) {
cout << setw(4) << tmp->data;
tmp = tmp->next;
}
cout << endl;
}
//获取队首元素,不出队
int getHeadElem(LinkQueue* LQ, DataType* data) {
if (!LQ || IsEmpty(LQ)) {
cout << "队列为空!" << endl;
return 0;
}
if (!data)return 0;
*data = LQ->front->data;
return 1;
}
//清空队列
void ClearQueue(LinkQueue* LQ) {
if (!LQ)return;
while (LQ->front) {
QueuePtr tmp = LQ->front->next;
delete LQ->front;
LQ->front = tmp;
}
LQ->front = LQ->rear = NULL;
LQ->length = 0;
}
//获取队列中元素的个数
int getLength(LinkQueue* LQ) {
if (!LQ)return 0;
return LQ->length;
}
//资源清理
void Delete(LinkQueue*& LQ) {
delete LQ;
}
int main(void) {
LinkQueue* LQ = new LinkQueue;
DataType data= -1;
//初始化队列
InitQueue(LQ);
//入队
for (int i = 0; i < 7; i++) {
EnterQueue(LQ, i);
}
//打印队列中的元素
cout << "队列中的元素个数(总共)" << getLength(LQ) << "个" << endl;
PrintQueue(LQ);
//出队
if (DeleteQueue(LQ, &data)) {
cout << "出队的元素是:" << data << endl;
}
else {
cout << "出队失败!" << endl;
}
//打印队列中的元素
cout << "出队一个元素后,队列中剩下的元素个数:" << LQ->length << endl;
PrintQueue(LQ);
//获取队首元素
if (getHeadElem(LQ, &data)) {
cout << "队首元素为:" << data << endl;
}
else {
cout << "获取队首元素失败!" << endl;
}
//清空队列
cout << "清空队列" << endl;
ClearQueue(LQ);
//资源清理
Delete(LQ);
return 0;
}
小结:
对于队列的顺序存储说,出队时,如果采用第一种(将队列中队头的元素data出队,后面的元素向前移动)方式,显然效率很低,相较于第二种(队头指针front后移一位)方式。但是,第二种出队方式对于空间的利用不是很好,当所有元素出队后,队列不能再起作用。
思考,有没有一种方法可以让数组中出队元素之前的空间很好的利用起来呢?答案肯定是有的,此时,使用循环队列就可以很好地解决空间浪费的问题。而且,相比较于队列的链式存储,不再需要额外的动态分配空间,利用已经分配好的空间就足够了。
循环队列:
循环队列是把 顺序队列 首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。 循环队列就是将 队列 存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。 在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。
循环队列原理图:(原文链接:https://blog.csdn.net/u010429311/article/details/51043149)
我们可以发现,当循环队列属于上图的d1情况时,是无法判断当前状态是队空还是队满。为了达到判断队列状态的目的,可以通过牺牲一个存储空间来实现。
如上图d2所示,
队头指针在队尾指针的下一位置时,队满。 Q.front == (Q.rear + 1) % MaxSize 因为队头指针可能又重新从0位置开始,而此时队尾指针是MaxSize - 1,所以需要求余。
当队头和队尾指针在同一位置时,队空。 Q.front == Q.rear
算法实现过程:(在上面顺序队列的基础上稍作修改)
循环队列的初始化及判断队列是否为空同上非循环顺序队列的初始化及判断队列是否为空
1.判断循环队列是否为满
//判断循环队列为满
int IsFull(SeqQueue* SQ) {
if (!SQ)return 0;
if ((SQ->rear + 1) % MaxSize == SQ->front) {
return 1;
}
return 0;
}
2.入队,将元素data插入到循环队列SQ中
注意:队尾指针循环后移一位时的操作
//入队,将元素data插入到循环队列SQ中
int EnterQueue(SeqQueue* SQ, DataType data) {
if (!SQ)return 0;
if (IsFull(SQ)) {
cout << "无法插入元素" << data << ",循环队列已满!" << endl;
return 0;
}
SQ->queue[SQ->rear] = data;//在队尾插入元素data
SQ->rear = (SQ->rear+1)%MaxSize;//队尾指针循环后移一位
return 1;
}
3.出队,将循环队列中队头的元素data出队,出队后,队头指针front后移一位
注意:队首指针循环后移一位时的操作
//出队,将循环队列中队头的元素data出队,出队后,队头指针front后移一位
int DeleteQueue(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
*data = SQ->queue[SQ->front];//出队元素值
SQ->front = (SQ->front+1)%MaxSize;//队首指针循环后移一位
return 1;
}
4.打印循环队列中的各元素
注意:i值循环增加时的操作以及循环条件的判断
//打印循环队列中的各元素
void PrintQueue(SeqQueue* SQ) {
if (!SQ)return;
int i = SQ->front;
while (i != SQ->rear) {
cout << setw(4) << SQ->queue[i];
i = (i+1)%MaxSize;
}
cout << endl;
}
5.获取队列中的元素个数
//获取队列中的元素个数
int GetCount(SeqQueue* SQ) {
if (!SQ)return 0;
return (SQ->rear - SQ->front + MaxSize) % MaxSize;
}
完整代码实现:
#include <iostream>
#include <iomanip>
#define MaxSize 5 //循环队列的最大容量
using namespace std;
typedef int DataType;//循环队列中元素类型
typedef struct Queue {
DataType queue[MaxSize];
int front;//循环队头"指针"
int rear; //循环队尾"指针"
}SeqQueue;
//循环队列初始化,将循环队列初始化为空队列
void InitQueue(SeqQueue* SQ) {
if (!SQ)return;
SQ->front = SQ->rear = 0;//把队头和队尾指针同时置0
}
//判断循环队列为空
int IsEmpty(SeqQueue* SQ) {
if (!SQ)return 0;
if (SQ->rear == SQ->front) {
//cout << "空队列!" << endl;
return 1;
}
return 0;
}
//判断循环队列为满
int IsFull(SeqQueue* SQ) {
if (!SQ)return 0;
if ((SQ->rear + 1) % MaxSize == SQ->front) {
//cout << "队列已满!" << endl;
return 1;
}
return 0;
}
//入队,将元素data插入到循环队列SQ中
int EnterQueue(SeqQueue* SQ, DataType data) {
if (!SQ)return 0;
if (IsFull(SQ)) {
cout << "无法插入元素" << data << ",循环队列已满!" << endl;
return 0;
}
SQ->queue[SQ->rear] = data;//在队尾插入元素data
SQ->rear = (SQ->rear+1)%MaxSize;//队尾指针循环后移一位
return 1;
}
//打印循环队列中的各元素
void PrintQueue(SeqQueue* SQ) {
if (!SQ)return;
int i = SQ->front;
while (i != SQ->rear) {
cout << setw(4) << SQ->queue[i];
i = (i+1)%MaxSize;
}
cout << endl;
}
//出队,将循环队列中队头的元素data出队,出队后,队头指针front后移一位
int DeleteQueue(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
*data = SQ->queue[SQ->front];//出队元素值
SQ->front = (SQ->front+1)%MaxSize;//队首指针循环后移一位
return 1;
}
//获取队首元素,不出队
int GetHead(SeqQueue* SQ, DataType* data) {
if (!SQ || IsEmpty(SQ)) {
cout << "队列为空!" << endl;
return 0;
}
*data = SQ->queue[SQ->front];
return *data;
}
//清空队列
void ClearQueue(SeqQueue* SQ) {
if (!SQ)return;
SQ->front = SQ->rear = 0;
}
//获取队列中的元素个数
int GetCount(SeqQueue* SQ) {
if (!SQ)return 0;
return (SQ->rear - SQ->front + MaxSize) % MaxSize;
}
int main(void) {
SeqQueue* SQ = new SeqQueue;
DataType data;
//1.初始化队列
InitQueue(SQ);
//2.入队
for (int i = 0; i < 7; i++) {
EnterQueue(SQ, i);
}
//打印队列中的元素
cout << "队列中的元素:";
PrintQueue(SQ);
cout << endl;
//出队
for (int i = 0; i < 5; i++) {
if (DeleteQueue(SQ, &data)) {
cout << "出队的元素是:" << data << endl;
cout << "出队一个元素后,剩下的元素:";
PrintQueue(SQ);
}
}
cout << endl;
return 0;
}