一、基本概念
队列(Queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
队列基本操作:
InitQueue() ——初始化队列
EnQueue() ——进队列
DeQueue() ——出队列
IsQueueEmpty() ——判断队列是否为空
IsQueueFull() ——判断队列是否已满
队列可以由数组和链表两种形式实现队列操作。分别称为顺序队列和链队列。
顺序队列:顺序存储的队列。
链队列:链式存储的队列,长度没限制。
二、顺序队列
我们一直都是用数组来实现顺序存储的,顺序队列也不例外。所以我们可以定义一个数组int data[MAXSIZE]
来存储队列的元素。另外,我们还需要两个指针,来标记队头和队尾。
实现要点:
(1)顺序队列初始化:就是把队头和队尾都归0,也就是 Q->front=0; Q->rear=0;
每当插入新的队列尾元素时,“尾指针增1”;每当删除队列头元素时,“头指针增1”。因此,非空队列中,头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一位置。如图:
(2)入队的算法应该怎么写?队列是线性表,用数组实现,因此首先要判断队列是不是满的。
如何判断一个队列是否满的?
假设我们在军训中排队,每个人报数。一个队列只能站10个人,从1报到10,队就满了。后来呢,队头的两个人出队了,然后又补充了两个新队员,那么这时候的报数是3到12。这时队列也是满的。两种情况下判断队满的条件分别为:
(10 + 1) mod 10 = 1
(12 + 1) mod 10 = 3
所以队列满的条件就是 :
(Q->rear+1)%MAXSIZE == Q->front
(3)如果队不满,我们就可以入队了。
思路就是,先给队尾元素赋值,然后再将队尾指针向后移动一位。
比如从空队列开始,此时 Q->front == Q->rear,这个时候插入元素的话,其实就是给 data[Q->rear] 赋值 e;然后队尾指针 Q->rear 向后移动一位重新赋值,使用
Q->rear = (Q->rear+1)%MAXSIZE;
即可完成尾指针后移。入队操作:
/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(SqQueue *Q,QElemType e)
{
if ((Q->rear+1)%MAXSIZE == Q->front) /* 队列满的判断 */
return ERROR;
Q->data[Q->rear]=e; /* 将元素e赋值给队尾 */
Q->rear=(Q->rear+1)%MAXSIZE;/* rear指针向后移一位置, */
/* 若到最后则转到数组头部 */
return OK;
}
(4)出队操作 。
首先,队列空的判断依据:Q->front == Q->rear
只要将队头指针向后移动一位就可以完成出队 Q->front=(Q->front+1)%MAXSIZE;
在这之前需要用 e 来保存出队的元素。出队函数如下:
/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(SqQueue *Q, QElemType *e)
{
if (Q->front == Q->rear) /* 队列空的判断 */
return ERROR;
*e=Q->data[Q->front]; /* 将队头元素赋值给e */
Q->front=(Q->front+1)%MAXSIZE; /* front指针向后移一位置, */
/* 若到最后则转到数组头部 */
return OK;
}
(5)置空顺序队列。只要让队头指针与队尾指针相等即可。
/* 将Q清为空队列 */
Status ClearQueue(SqQueue *Q)
{
Q->front=Q->rear=0;
return OK;
}
(6)获取队列长度。可以使用模运算来获取队列的长度。具体算法:队尾指针 - 队头指针 + 数组长度的和再模数组长度即可。
/* 返回Q的元素个数,也就是队列的当前长度 */
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}
顺序队列实现代码如下:
#include <iostream>
using namespace std;
#define maxsize 20 /* 存储空间初始分配量 */
typedef int QElemType;
//循环队列顺序存储结构
typedef struct
{
QElemType a[maxsize];
int front;//队列头指针
int rear;//队列尾指针,若队列不空,指向队列尾元素的下一个位置
}SqQueue;
//队列初始化函数
void InitQueue(SqQueue *q)
{
q->front = q->rear = 0;
}
//判断队列是否为空
bool IsEmpty(SqQueue *q)
{
return q->front == q->rear;
}
//判断队列是否已满
bool IsFull(SqQueue *q)
{
return (q->rear + 1)%maxsize == q->front;
}
//获取队列长度
void QueueLength(SqQueue *q)
{
int length = (q->rear - q->front + maxsize) % maxsize;
cout<<"队列长度:"<<length<<endl;
}
//入队
void EnQueue(SqQueue *q, QElemType x)
{
if(IsFull(q))
{
cout<<"队列已满!"<<endl;
}
else
{
q->a[q->rear] = x;//先把入队值赋给队尾指针指向的地方(原队列队尾的下一位置)
q->rear = (q->rear+1)%maxsize;//队尾指针后移
}
}
//出队
void DeQueue(SqQueue *q)
{
QElemType x;
if(IsEmpty(q))
{
cout<<"队列已空!"<<endl;
}
else
{
x = q->a[q->front];
cout<<"delete x="<<x<<endl;
q->front = (q->front+1)%maxsize;
}
}
//将队列清空
void ClearQueue(SqQueue *q)
{
cout<<"请空队列!"<<endl;
q->front = q->rear = 0;
}
//打印队列
void PrintQueue(SqQueue *q)
{
if(IsEmpty(q))
{
cout<<"队列为空,无法输出!"<<endl;
}
for(int i=q->front; i%maxsize < q->rear; i++)
{//q->rear指向尾元素的下一位置!!!
if(i == q->rear-1)
{//遍历到尾元素
cout<<q->a[i]<<endl;
}
else
{
cout<<q->a[i]<<" ";
}
}
}
int main()
{
SqQueue *q;
InitQueue(q);
EnQueue(q, 1);
PrintQueue(q);
EnQueue(q, 2);
PrintQueue(q);
EnQueue(q, 3);
PrintQueue(q);
EnQueue(q, 4);
PrintQueue(q);
QueueLength(q);
DeQueue(q);
PrintQueue(q);
DeQueue(q);
PrintQueue(q);
QueueLength(q);
ClearQueue(q);
QueueLength(q);
PrintQueue(q);
return 0;
}
结果:
1
1 2
1 2 3
1 2 3 4
队列长度:4
delete x=1
2 3 4
delete x=2
3 4
队列长度:2
请空队列!
队列长度:0
队列为空,无法输出!
Process returned 0 (0x0) execution time : 5.301 s
Press any key to continue.