目录
1.队列的概念
(1)队列的定义
队列简称队,也是一种操作受限的线性表,它只允许在一端进行插入操作,在另一端进行删除操作的线性表。其中允许插入(也称入队,进队)的一端称为队尾(Rear),允许删除(也称出队)的一端称为队头(Front)。
(2)队列的特点
如下图所示,有一个有5个元素的队列,入队的顺序为a1、a2、a3、a4、a5,出队的顺序依然是a1、a2、a3、a4、a5,即最先入队者最先出队。所以队列中的元素除了具有线性关系外,还具有先进先出(First In First Out,简称FIFO)的特性。
队列可以看做是一列开进隧道的火车,各节车厢就是队中的元素,最先开进隧道的车厢总是最先驶出隧道。
(3)队列的存储结构
与栈类似,我们可以用顺序表和链表来存储队列,队列按存储结构可以分为顺序队和链队两种。
2.队列的基本操作
(1)顺序队
①顺序队的定义
根据队列的定义,我们知道顺序栈是队列的顺序存储结构,这里我们先来看一下顺序队的实际操作。如图,开始时队列为空,队头和队尾指针处于初始化位置。接着,a1、a2、a3、a4、a5依次入队,队尾指针移动,同时队列已满。然后,a1、a2元素出队,队头指针移动,但是此时,尽管队列中还有两个空位置,由于队列的性质,无法再有元素入队,这就出现了“假溢出”的现象。为了解决这一问题,我们选择使用将队头与队尾连接,形成循环队列。
如下图所示:
①空队进队两个元素,此时Front指向0,Rear指向2。
②进队4个元素,出队3个元素,此时Front指向3,Rear指向6。
③进队2个元素,出队4个元素,此时Front指向7,Rear指向0。
#define MaxSize 100
typedef struct QUEUE
{
int Array[MaxSize]; //顺序表
int Front; //队头指针
int Rear; //队尾指针
}SqlQueue,*PSqlQueue;
②顺序队的状态
顺序队一共有三种状态:队为空,队满和正常状态。
队空时,Front == Rear,队内没有元素。队满时,(Rear+1)%MaxSize == Front,其中MaxSize为队最多容纳的元素个数,取余是考虑到可能会队头和队尾连接处的临界情况。
void GetQueueStatus(SqlQueue Queue)
{
if (Queue.Front == Queue.Rear)
{
printf("当前队列为空,无法执行出队操作!\r\n");
}
else if ((Queue.Rear + 1) % MaxSize == Queue.Front)
{
printf("当前队列已满,无法执行入队操作!\r\n");
}
else
{
printf("当前队列正常,可以执行入队或出队操作!\r\n");
}
}
③顺序队的入队
BOOL Enqueue(SqlQueue &Queue, int Data)
{
//首先判断是否队列已满
if ((Queue.Rear + 1) % MaxSize == Queue.Front)
{
return FALSE;
}
else
{
//先移动队尾指针,再执行入队操作
Queue.Rear = (Queue.Rear + 1) % MaxSize;
Queue.Array[Queue.Rear] = Data;
return TRUE;
}
}
④顺序队的出队
int Dequeue(SqlQueue &Queue)
{
//判断是否队空
if (Queue.Front == Queue.Rear)
{
return -1;
}
else
{
//先系统队头指针,再返回队头元素
Queue.Front = (Queue.Front + 1) % MaxSize;
return Queue.Array[Queue.Front];
}
}
(2)链队
①链队的定义
链队与单链表结构类似,其中队头指针和队尾指针类似于单链表的头指针和尾指针,只是在元素的插入和删除上做了稍微的限制,即队列的入队和出队。入队类似于单链表的尾插法,出队则是单链表的头部结点的删除。
//保存数据信息的结点
typedef struct QUEUE
{
int Data;
struct QUEUE* Flink;
}QueueNode,*PQueueNode;
//保存队头指针和队尾指针
typedef struct QUEUEINFORMATION
{
PQueueNode Front;
PQueueNode Rear;
}QueueInformation,*PQueueInformation;
②链队的状态
顺序队一共有两种状态:队为空和正常状态。
队空时,Front = Rear = NULL,队内没有元素。正常状态时,队中可执行正常的入队和出队操作。
void GetQueueStatus(QueueInformation QueueInfomation)
{
if (QueueInfomation.Front == NULL && QueueInfomation.Rear == NULL)
{
printf("当前队列为空,无法执行出队操作!\r\n");
}
else
{
printf("当前队列正常,可以执行入队或出队操作!\r\n");
}
}
③链队的入队
void Enqueue(QueueInformation &QueueInfomation, int Data)
{
//申请内存
PQueueNode v1 = new QueueNode;
v1->Data = Data;
//如果队空,同时让队头和队尾指针指向同一结点
if (QueueInfomation.Front == NULL && QueueInfomation.Rear == NULL)
{
QueueInfomation.Front = QueueInfomation.Rear = v1;
v1->Flink = NULL;
}
else
{
//尾插法
QueueInfomation.Rear->Flink = v1;
QueueInfomation.Rear = v1;
v1->Flink = NULL;
}
}
④链队的出队
int Dequeue(QueueInformation &QueueInfomation)
{
//两种情况下队头指针和队尾指针相等
//1.队空,两指针都为空 2.队内只有一个结点
if (QueueInfomation.Front == QueueInfomation.Rear)
{
if (QueueInfomation.Front == NULL && QueueInfomation.Rear == NULL)
{
return -1;
}
else
{
//临时变量保存唯一结点和数据,同时队头队尾指针赋空并返回数据
PQueueNode v1 = QueueInfomation.Front;
int v2 = v1->Data;
QueueInfomation.Front = QueueInfomation.Rear = NULL;
delete v1;
return v2;
}
}
else
{
//指向正常的头部节点的删除操作
PQueueNode v1 = QueueInfomation.Front;
int v2 = v1->Data;
QueueInfomation.Front = v1->Flink;
delete v1;
return v2;
}
}
(3)两种队的性能比较
实现循环队列和链队列的基本操作的算法都需要常数时间O(1)。循环队列和链队列的空间性能的比较与顺序栈和链栈的空间性能的比较类似,只是循环队列不能像顺序栈那样共享空间,通常不能在一个数组中存储两个循环队列。
3.参考代码
(1)顺序队
#include<Windows.h>
#include<tchar.h>
#include <iostream>
using namespace std;
#define MaxSize 100
typedef struct QUEUE
{
int Array[MaxSize];
int Front;
int Rear;
}SqlQueue,*PSqlQueue;
void GetQueueStatus(SqlQueue Queue)
{
if (Queue.Front == Queue.Rear)
{
printf("当前队列为空,无法执行出队操作!\r\n");
}
else if ((Queue.Rear + 1) % MaxSize == Queue.Front)
{
printf("当前队列已满,无法执行入队操作!\r\n");
}
else
{
printf("当前队列正常,可以执行入队或出队操作!\r\n");
}
}
BOOL Enqueue(SqlQueue &Queue, int Data)
{
if ((Queue.Rear + 1) % MaxSize == Queue.Front)
{
return FALSE;
}
else
{
Queue.Rear = (Queue.Rear + 1) % MaxSize;
Queue.Array[Queue.Rear] = Data;
return TRUE;
}
}
int Dequeue(SqlQueue &Queue)
{
if (Queue.Front == Queue.Rear)
{
return -1;
}
else
{
Queue.Front = (Queue.Front + 1) % MaxSize;
return Queue.Array[Queue.Front];
}
}
int main()
{
SqlQueue Queue;
Queue.Front = Queue.Rear = 0;
int i = 0;
GetQueueStatus(Queue);
for (i = 1; i < 100; i++)
{
Enqueue(Queue, i);
}
GetQueueStatus(Queue);
printf("第一次出队的十个元素为:");
for (i = 1; i < 10; i++)
{
int v1 = Dequeue(Queue);
printf("%d ", v1);
}
printf("\r\n");
printf("第二次出队的十个元素为:");
for (i = 1; i < 10; i++)
{
int v1 = Dequeue(Queue);
printf("%d ", v1);
}
printf("\r\n");
GetQueueStatus(Queue);
}
测试结果
(2)链队
#include<Windows.h>
#include<tchar.h>
#include <iostream>
using namespace std;
typedef struct QUEUE
{
int Data;
struct QUEUE* Flink;
}QueueNode,*PQueueNode;
typedef struct QUEUEINFORMATION
{
PQueueNode Front;
PQueueNode Rear;
}QueueInformation,*PQueueInformation;
void GetQueueStatus(QueueInformation QueueInfomation)
{
if (QueueInfomation.Front == NULL && QueueInfomation.Rear == NULL)
{
printf("当前队列为空,无法执行出队操作!\r\n");
}
else
{
printf("当前队列正常,可以执行入队或出队操作!\r\n");
}
}
void Enqueue(QueueInformation &QueueInfomation, int Data)
{
PQueueNode v1 = new QueueNode;
v1->Data = Data;
if (QueueInfomation.Front == NULL && QueueInfomation.Rear == NULL)
{
QueueInfomation.Front = QueueInfomation.Rear = v1;
v1->Flink = NULL;
}
else
{
QueueInfomation.Rear->Flink = v1;
QueueInfomation.Rear = v1;
v1->Flink = NULL;
}
}
int Dequeue(QueueInformation &QueueInfomation)
{
if (QueueInfomation.Front == QueueInfomation.Rear)
{
if (QueueInfomation.Front == NULL && QueueInfomation.Rear == NULL)
{
return -1;
}
else
{
PQueueNode v1 = QueueInfomation.Front;
int v2 = v1->Data;
QueueInfomation.Front = QueueInfomation.Rear = NULL;
delete v1;
return v2;
}
}
else
{
PQueueNode v1 = QueueInfomation.Front;
int v2 = v1->Data;
QueueInfomation.Front = v1->Flink;
delete v1;
return v2;
}
}
int main()
{
QueueInformation QueueInformation;
QueueInformation.Front = QueueInformation.Rear = NULL;
int i = 0;
GetQueueStatus(QueueInformation);
for (i = 1; i <= 20; i++)
{
Enqueue(QueueInformation, i);
}
printf("第一次出队的十个元素为:");
for (i = 0; i < 10; i++)
{
int v1 = Dequeue(QueueInformation);
printf("%d ", v1);
}
printf("\r\n");
GetQueueStatus(QueueInformation);
printf("第二次出队的十个元素为:");
for (i = 0; i < 10; i++)
{
int v1 = Dequeue(QueueInformation);
printf("%d ", v1);
}
printf("\r\n");
GetQueueStatus(QueueInformation);
}