队列的基本知识
队列(ADT):限定在表的一端插入,在表的另一端删除的线性表。其中,队尾(rear)为新元素依次进队的位置,队头(front)则为队列中元素依次出队的位置。 用白话来说就是一群人排队买饭,前头的人买完饭就走,这是出队,后面来的的人加入排队买饭,这是入队。最前头的是队头,最后面的是队尾。
队列是先进先出(First In First Out,FIFO)的线性数据结构。 这和买饭是一个情况的,先到的人先买到饭。与队列先进先出相反的是,栈是后进先出。
一.顺序队列
队列的顺序表示我这里使用一维数组来实现,其数据结构定义如下:
typedef struct//定义一个队列
{
int data[MaxSize];//定义一个数组
int front, rear;//定义队头指针和队尾指针
}SqQueue;
需要注意的是,front指向的是队头元素前一个单元的下标位置,rear指向的是队尾元素的下标位置。MaxSize表示队列中最多允许存储的元素数量。
一开始的时候 f 和 r 会设置指向-1的位置。 初始时,空队列的顺序表示如下图的(a)所示(为了演示方便,图中 f 表示front,r 表示rear)。元素入队时先将队尾下标加1,然后元素入队;元素出队时,先将队头下标加1,然后元素出队。元素a,b,c依次入队后的情况如下图(b)所示,每有一个元素入队,r 就+1一次,因为下图(b)入队了三个元素,于是 r+3=4。执行两次元素出队操作后的情况如下图(c)所示,每出队一次,f 就+1一次,因为下图(c)出队了两个元素,于是 f+2=1;下图(d)所示是元素d入队后的情况,入队一个元素,r+1=3。
从下图(d)可以看到,当再有元素需要入队时,将产生“溢出”,但很显然队列中还有2个空的存储单元,这种现象被称为“假溢出”。这种“假溢出”的现象的发生,说明上述存储方式是有缺陷的。那么就对上述存储方式进行改进,我们常用的一种改进方法是采用循环队列结构,即把数组从逻辑上看成一个头尾相连的环。当有新元素入队时,将该新元素存入环形结构的下一个单元位置。这里我不做详细讲解,我会在这篇博客的循环队列进行详细讲解,可以翻目录快速到达哟。
基本操作
循环队列有八大操作:初始化,销毁,入队,出队,判空,判满,读取队头元素,输出队列所有元素。
1.初始化
顺序队列的初始化只需要让队头指针和队尾指针指向-1的位置就好了,指向-1是因为我们用的是一维数组来实现这个顺序队列。
void InitQueue(SqQueue &Q)//初始化队列
{
Q.front = Q.rear = -1;
}
2.销毁
销毁操作和初始化操作是一样的,也是让队头指针和队尾指针指向-1就好了,可以在前面加上是否销毁的判断代码。
void DestroyQueue(SqQueue &Q)//销毁队列
{
char a;
printf("是否销毁队列(Y/N):");
getchar();
scanf("%c", &a);
if (a == 'Y')
{
Q.front = Q.rear = -1;
printf("队列销毁成功\n");
}
else
printf("队列销毁失败\n");
}
3.入队
入队就是元素进入队列,每入队一次,队尾指针rear就+1一次。需要注意的是,队尾指针rear先+1,然后才进行输入,把元素放入队尾指针rear现在所指向的位置。这个与下面的出队操作刚好相反。
我们可以在入队前加一个入队确认的代码,出队的时候也可以加上这个,这里用getchar()处理掉之前遗留的字符。
bool EnQueue(SqQueue &Q)//入队
{
char a;
printf("是否进行入队(Y/N):");
getchar();
scanf("%c", &a);
if (a == 'N')
return false;
if (QueueFull(Q))//判断队列是否满了,如果队列满了就无法进行入队操作,直接返回return false。
{
printf("队满,无法进行入队\n");
return false;
}
printf("请输入一个要入队的数字:");
int x;
scanf("%d", &x);
Q.rear = Q.rear + 1;//队尾指针往前移动一格
Q.data[Q.rear] = x;//把元素x放入此时队尾指针指向的队列位置
printf("入队成功,入队的元素是%d\n", x);
return true;
}
4.出队
出队就是删除队列里面的元素,每出队一次,队头指针front就+1一次。需要注意的是,直接让队头指针front往前移动1个位置,这个元素就被删除了,与入队同样的是,在一开始可以加上出队确认的代码。
bool DeQueue(SqQueue &Q)//出队
{
char a;
printf("是否进行出队(Y/N):");
getchar();
scanf("%c", &a);
if (a == 'N')
return false;
if (QueueEmpty(Q))//判断队列是否为空,如果队列为空就无法进行出队操作,直接返回return false。
return false;
printf("出队成功,出队的元素是:%d\n",Q.data [Q.front+1]);
Q.front = Q.front + 1;//队头指针往前移动一格
return true;
}
5.判空
判断队空就是判断队尾指针和队头指针有没有指向同一个位置。
bool QueueEmpty(SqQueue &Q)//判断队空
{
return (Q.rear == Q.front);
}
6.判满
判断队满直接判断队尾指针rear有没有指向队列最后一个位置就可以了。 这样子判断就足够了,不需要再加上队头指针rear指向-1这个条件,因为假溢出本来就是当作队满情况对待的。
bool QueueFull(SqQueue &Q)//判断队满
{
return (Q.rear == MaxSize - 1);
}
7.读取队头元素
读取队头元素前要先对队列进行判空操作,队列里要有元素才能进行读取队头操作。由概念可知,队头元素就是队头指针前面的那个位置的元素。所以直接打印 Q.data[Q.front +1]就好了。
bool GetHead(SqQueue &Q)//读取队头元素
{
if (QueueEmpty(Q))
return false;
printf("队头元素是:%d\n", Q.data [Q.front +1]);
return true;
}
8.输出队列所有元素
输出队列所有的元素,只需要用一个for循环,从队头元素到队尾元素一个个输出就好了。
bool print(SqQueue &Q)//输出队列元素
{
if (QueueEmpty(Q))
return false;
printf("队列的元素是:");
for (int i = Q.front + 1; i <= Q.rear; i++)
{
printf("%d ", Q.data[i]);
}
printf("\n");
return true;
}
全部代码及实现结果
#define MaxSize 10
#include<stdio.h>
typedef struct
{
int data[MaxSize];
int front, rear;
}SqQueue;
void InitQueue(SqQueue &Q)//初始化队列
{
Q.front = Q.rear = -1;
}
void DestroyQueue(SqQueue &Q)//销毁队列
{
char a;
printf("是否销毁队列(Y/N):");
getchar();
scanf("%c", &a);
if (a == 'Y')
{
Q.front = Q.rear = -1;
printf("队列销毁成功\n");
}
else
printf("销毁失败\n");
}
bool QueueEmpty(SqQueue &Q)//判断队空
{
return (Q.rear == Q.front);
}
bool QueueFull(SqQueue &Q)//判断队满
{
return (Q.rear == MaxSize - 1);
}
bool TakeQueue(SqQueue &Q)//在队列里面放入元素
{
int i;
printf("请输入要创建队列的长度:");
scanf("%d", &i);
if (i > MaxSize || i < 0)
{
printf("创建失败\n");
return false;
}
printf("请输入%d个数字:",i);
for (int j = 0; j < i; j++)
{
Q.rear = Q.rear + 1;
scanf("%d", &Q.data[Q.rear]);
}
return true;
}
bool EnQueue(SqQueue &Q)//入队
{
char a;
printf("是否进行入队(Y/N):");
getchar();
scanf("%c", &a);
if (a == 'N')
return false;
if (QueueFull(Q))
{
printf("队满,无法进行入队\n");
return false;
}
printf("请输入一个要入队的数字:");
int x;
scanf("%d", &x);
Q.rear = Q.rear + 1;
Q.data[Q.rear] = x;
printf("入队成功,入队的元素是%d\n", x);
return true;
}
bool GetHead(SqQueue &Q)//读取队头元素
{
if (QueueEmpty(Q))
return false;
printf("队头元素是:%d\n", Q.data [Q.front +1]);
return true;
}
bool DeQueue(SqQueue &Q)//出队
{
char a;
printf("是否进行出队(Y/N):");
getchar();
scanf("%c", &a);
if (a == 'N')
return false;
if (QueueEmpty(Q))
return false;
printf("出队成功,出队的元素是:%d\n",Q.data [Q.front+1]);
Q.front = Q.front + 1;
return true;
}
bool Print(SqQueue &Q)//输出队列元素
{
if (QueueEmpty(Q))
return false;
printf("队列的元素是:");
for (int i = Q.front + 1; i <= Q.rear; i++)
{
printf("%d ", Q.data[i]);
}
printf("\n");
return true;
}
int main()
{
SqQueue(Q);
InitQueue(Q);//初始化队列
TakeQueue(Q);//在队列里面放入元素