数据结构——队列

在数据结构中,队列(Queue)是一种非常基础且重要的线性数据结构,它遵循 "先进先出"(First-In-First-Out,FIFO)的原则,就像我们日常生活中排队买票一样,先到达的人先得到服务,后到达的人只能在队尾依次排队。本文将从队列的基本概念、核心操作、常见实现方式以及实际应用场景等方面,全面且深入地为大家讲解队列这种数据结构。

一. 概念和结构

1.1 概念

队列是一种特殊的线性表,它只允许在表的一端进行插入操作,在表的另一端进行删除操作。遵循**"先进先出"(First-In-First-Out,FIFO)的原则**

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

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

底层结构选择

队列的底层用链表实现比较好,如果使用顺序表,出队列是的时间复杂度比较高,效率低

1.2 结构特性

  1. 先进先出(FIFO):这是队列最核心的特性,即最早进入队列的元素,会最早从队列中删除。
  2. 线性结构:队列的元素之间存在一对一的线性关系,除了队头和队尾元素外,每个元素都有唯一的前驱元素和后继元素。
  3. 操作受限:与普通线性表不同,队列的插入和删除操作被严格限制在队尾和队头,不允许在队列中间进行插入或删除操作。

二. 队列的实现

Queue.h(队列结构定义和函数定义)

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
//定义队列节点结构
typedef struct QueueNode {
	QDataType data;
	struct QueueNode* next;
}QueueNode;

//定义队列结构
typedef struct Queue {
	QueueNode* phead;//对头
	QueueNode* ptail;//队尾
	//int size;        //队列中有效数据个数
}Queue;

//初始化
void QInit(Queue* pq);

//销毁
void QueueDestory(Queue* pq);

//不存在头插尾插
//入队列------队尾入
void QueuePush(Queue* pq, QDataType x);

//出队列------队头出
void QueuePop(Queue* pq);

//判断队列是否为空
bool QueueEmpty(Queue* pq);

//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);

//队列有效元素个数
int QueueSize(Queue* pq);

Queue.c(函数实现)

复制代码
//初始化
void QInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
}


//入队------队尾入
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建值为x的节点
	QueueNode* newnode =(QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	//队列为空
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else {
		pq->ptail->next = newnode;
		pq->ptail = pq->ptail->next;
	}
}

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->phead == NULL;
	//相等返回true,不相等返回false 
}

//出队------队头出
void QueuePop(Queue* pq)
{
	//判断队列是否为空
	assert(!QueueEmpty(pq)); 
	//队列中只有一个节点
	if (pq->phead == pq->ptail)
	{
		pq->phead = pq->ptail = NULL;
	}
	else {
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
}

//取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}

//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->ptail->data;
}

//队列有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	QueueNode* pcur = pq->phead;
	int size = 0;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}
	return size; 

	//该方法时间复杂度为O(N),如果数据过多,效率太低
	//优化方法:在定义队列结构是,加入一个变量size,入队时,pq->size++,出队时,pq->size---.
	//最后return pq->size即可
	//return pq->size;
}

//销毁
void QueueDestory(Queue* pq)
{
	assert(pq);
	QueueNode* pcur = pq->phead;
	while (pcur)
	{
		QueueNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
}

:可自行创建测试文件去测试并实现一下以上代码,加深理解)

三. 队列的应用场景

3.1 任务调度​

在操作系统中,队列常用于任务调度。例如,进程调度中的就绪队列,当多个进程都处于就绪状态等待 CPU 执行时,操作系统会按照一定的调度算法(如先来先服务调度算法),将这些进程依次放入就绪队列中,CPU 从队头取出进程进行执行,执行完成后再取下一个进程,这样就保证了进程按照 "先进先出" 的顺序得到执行(在先来先服务调度算法下)。​

此外,在线程池、任务队列等场景中,也会使用队列来管理待执行的任务。当有新的任务产生时,将任务加入到任务队列的队尾;线程池中的线程从任务队列的队头取出任务进行执行,从而实现任务的有序调度和执行。​

3.2 缓冲处理​

在数据传输和处理过程中,当数据的产生速度和处理速度不匹配时,通常会使用队列作为缓冲。例如,在网络通信中,数据从发送方发送到接收方的过程中,由于网络带宽、传输延迟等因素的影响,数据的到达速度可能会不稳定。此时,接收方会设置一个接收缓冲区(队列),将接收到的数据先放入队列中,然后处理程序再从队列中按照 "先进先出" 的顺序取出数据进行处理。这样可以避免因数据到达速度过快而导致的数据丢失,同时也可以使处理程序能够稳定、高效地处理数据。​

另外,在打印机打印文件的过程中,也会使用队列作为打印缓冲区。当多个用户同时向打印机发送打印请求时,这些打印请求会被依次放入打印队列中,打印机从队头取出打印请求进行打印,直到所有打印任务完成。​

3.3 广度优先搜索(BFS)​

在图和树的遍历算法中,广度优先搜索(Breadth-First Search,BFS)是一种非常重要的遍历方式,而队列是实现广度优先搜索的核心数据结构。​

以树的层序遍历为例,层序遍历是指按照树的层次,从根节点开始,依次访问每一层的所有节点,同一层的节点按照从左到右的顺序访问。实现层序遍历的步骤如下:​

  1. 将根节点入队。
  1. 当队列不为空时,取出队头节点,访问该节点。
  1. 将该节点的左子节点(如果存在)入队。
  1. 将该节点的右子节点(如果存在)入队。
  1. 重复步骤 2-4,直到队列为空,此时所有节点都已访问完毕。

在图的广度优先搜索中,原理与树的层序遍历类似,也是通过队列来记录待访问的节点,确保按照 "先访问距离起始节点近的节点,再访问距离远的节点" 的顺序进行遍历。​

3.4 消息队列​

在分布式系统和大型应用中,消息队列(Message Queue)是一种常用的组件,它基于队列的 "先进先出" 特性,实现了不同系统或模块之间的异步通信。​

例如,在电商平台中,当用户下单成功后,需要进行一系列的后续操作,如库存扣减、订单状态更新、发送短信通知、生成物流单等。如果这些操作都在用户下单的主线程中同步执行,会导致用户等待时间过长,影响用户体验。此时,可以使用消息队列来实现异步处理:当用户下单成功后,主线程只需要将订单信息发送到消息队列中,然后立即返回给用户下单成功的结果;而其他相关的处理模块(如库存模块、通知模块、物流模块等)则监听消息队列,当接收到订单消息后,各自从消息队列中取出消息进行相应的处理。这样不仅提高了系统的响应速度,还降低了各个模块之间的耦合度,提高了系统的稳定性和可扩展性。​

总结

队列在计算机科学领域有着广泛的应用,从操作系统的任务调度到分布式系统的消息队列,从数据缓冲处理到图和树的遍历算法,都离不开队列的支持。掌握队列的基本原理和实现方式,对于理解和设计高效的算法与系统具有重要的意义。​

希望本文能够帮助大家更好地理解和掌握队列这种数据结构,如果在学习过程中遇到任何问题,欢迎在评论区留言讨论!

相关推荐
码流之上32 分钟前
【一看就会一写就废 指间算法】设计电子表格 —— 哈希表、字符串处理
javascript·算法
用户6120414922131 小时前
C语言做的文本词频数量统计功能
c语言·后端·敏捷开发
快手技术2 小时前
快手提出端到端生成式搜索框架 OneSearch,让搜索“一步到位”!
算法
CoovallyAIHub1 天前
中科大DSAI Lab团队多篇论文入选ICCV 2025,推动三维视觉与泛化感知技术突破
深度学习·算法·计算机视觉
NAGNIP1 天前
Serverless 架构下的大模型框架落地实践
算法·架构
moonlifesudo1 天前
半开区间和开区间的两个二分模版
算法
moonlifesudo1 天前
300:最长递增子序列
算法
CoovallyAIHub1 天前
港大&字节重磅发布DanceGRPO:突破视觉生成RLHF瓶颈,多项任务性能提升超180%!
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
英伟达ViPE重磅发布!解决3D感知难题,SLAM+深度学习完美融合(附带数据集下载地址)
深度学习·算法·计算机视觉
聚客AI2 天前
🙋‍♀️Transformer训练与推理全流程:从输入处理到输出生成
人工智能·算法·llm