队列(顺序队列,循环队列,链式队列)超详细讲解

队列的基本知识

队列(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);//在队列里面放入元素
	
  • 30
    点赞
  • 123
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值