【数据结构】队列详解

Hello everybody!今天给大家讲讲队列的相关知识。队列,属于一种数据结构。从字面意思上理解,就像是排队一样,在食堂中,先排队的人自然就先买到饭。队列也是如此,先入队列的数据自然就先出队列。希望大家可以通过这篇文章解决自己心中的疑惑!那废话不多说,让我们开始叭!

                ​​​​​​​        ​​​​​​​      【数据结构】队列详解_第1张图片

1.队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列遵循先进先出FIFO(First In First Out)

入队列:进行插入操作的一端称为队尾

出队列:进行删除操作的一端称为队头

【数据结构】队列详解_第2张图片

2.队列的实现

队列可以以数组和链表的结构实现,使用链表的结构实现更优,因为如果使用数组的结构,出队列是需要移动数据,时间复杂度是O(n),效率比较低。

【数据结构】队列详解_第3张图片

【数据结构】队列详解_第4张图片

【数据结构】队列详解_第5张图片

由于实现队列需要较长的代码,因此我们要像公司中做大型项目一样创建一个头文件:Queue.h和两个源文件:Queue.c Test.c。

头文件中需要包含所需要的库文件,接口的声明,队列的声明。总之就是要我们清晰的看到队列的结构和功能。

而源文件Queue.c需要实现在头文件中声明的接口。

源文件Test.c用于测试队列的功能是否正常。

当然源文件需要包含头文件,这样才能把它们关联起来。

创建三个文件使得代码逻辑更加清晰,有利于后期代码的维护。

2.1接口的声明&队列的声明

【数据结构】队列详解_第6张图片

这是队列的结构,结构体QNode是队列的链式结构,它可以存放当前结点的值和下一个结点的地址。但是当队列为空,插入第一个数据时,需要改变头指针。因此需要传递二级指针,这样显然是有些麻烦的。所以我们再创建一个结构体Queue,用于存放队列的头指针和尾指针,更方便入队和出队。size用来记录队列中的元素个数。

【数据结构】队列详解_第7张图片

以上是队列主要功能的接口,下面我们来逐个实现它们。

2.2接口的实现及功能测试

【数据结构】队列详解_第8张图片

目前为止,我们已经实现了初始化和入队两个接口。下面测试一下它们的功能。

【数据结构】队列详解_第9张图片

通过调试,我们可以看到:初始化后,在队尾成功插入1和2。目前队列中有两个数值,接口功能正常。

下面我们来实现一下其他的接口:

【数据结构】队列详解_第10张图片

这是队列剩下的接口,咱们也来测试一下。

【数据结构】队列详解_第11张图片

从测试结果来看:各个接口功能正常,销毁队列后,头指针和尾指针都被置为空指针,队列大小size为0。符合要求。

3.代码

为了方便大家更好的学习,队列的代码我也给出来!

#pragma once
#include 
#include 
#include 
#include 
typedef int  QDataType;
typedef struct QueueNode {
	QDataType val;
	struct QueueNode* next;
}QNode;
typedef struct Queue {
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

void QueueInit(Queue* pq);//初始化
void QueuePush(Queue* pq, QDataType x);//入队
void QueuePop(Queue* pq);//出队
void QueueDestroy(Queue* pq);//销毁
QDataType QueueFront(Queue* pq);//返回队头的值
QDataType QueueBack(Queue* pq);//返回队尾的值
bool QueueEmpty(Queue* pq);//判断队列是否为空
int QueueSize(Queue* pq);//返回队列的数据个数
#include "Queue.h"
void QueueInit(Queue* pq) {
	assert(pq);//检验pq是否为空指针
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x) {
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));//动态开辟一个新结点
	if (newnode == NULL) {//若开辟失败,终止程序
		perror("malloc fail");
		exit(-1);
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pq->phead == NULL) {//如果队列中没有结点,则将新结点赋值给头指针和尾指针
		pq->phead = pq->ptail = newnode;
	}
	else {
		pq->ptail->next = newnode;//有结点的话,将新结点赋值给尾指针
		pq->ptail = newnode;
	}
	pq->size++;
}

void QueuePop(Queue* pq) {
	assert(pq);
	assert(pq->phead);//队列不能为空
	QNode* next = pq->phead->next;//记录头结点的下一个结点
	free(pq->phead);
	pq->phead = next;
	if (pq->phead == NULL) {//当phead为NULL时,ptail没有变化。但ptail指向的空间已经还给操作系统,此时ptail就是野指针,必须处理掉!
		pq->ptail = NULL;
	}
	pq->size--;
}
QDataType QueueFront(Queue* pq) {
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}
QDataType QueueBack(Queue* pq) {
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}
bool QueueEmpty(Queue* pq) {
	assert(pq);
	return pq->phead == NULL;
}
int QueueSize(Queue* pq) {
	assert(pq);
	return pq->size;
}
void QueueDestroy(Queue* pq) {
	assert(pq);
	while (pq->phead) {
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
#include "Queue.h"
int main() {
	Queue pq;
	QueueInit(&pq);
	QueuePush(&pq, 1);
	QueuePush(&pq, 2);
	QueuePush(&pq, 3);
	QueuePush(&pq, 4);
	printf("%d\n", QueueBack(&pq));
	printf("%d\n", QueueSize(&pq));
	while (!QueueEmpty(&pq)) {
		printf("%d", QueueFront(&pq));
		QueuePop(&pq);
	}
	QueueDestroy(&pq);
	return 0;
}

4.结语

好啦,关于队列的讨论就到这里叭。不知道大家有没有收获呢?如果有疑问,可以发在评论区。

最后,希望宝子们每天都有所进步,成为自己想成为的人,做自己想做的事!\(0^◇^0)/

你可能感兴趣的:(数据结构,c语言)