当前位置: 首页 > news >正文

队列的实现与应用详解

队列(Queue)是一种遵循先进先出(FIFO, First In First Out) 原则的线性数据结构,就像日常生活中排队买票的场景:先到的人先买票,后到的人排在队尾,只能等待前面的人处理完才能轮到自己。本文将从队列的结构设计、核心接口实现、功能测试等方面,详细讲解基于链表的队列实现。

一、队列的设计思路

队列的实现方式有两种:数组实现 和 链表实现。本文选择链表实现,原因是链表在队尾插入、队头删除操作时效率更高(无需移动元素),且不存在数组的容量限制问题。

1. 数据结构定义

队列的链表实现需要两个核心结构

  • 队列结点(QNode):存储单个数据元素,并指向后继结点;
  • 队列管理结构(Queue):记录队列的队头(phead)和队尾(ptail)指针,方便快速访问队头 / 队尾,避免遍历链表。

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

// 队列存储的数据类型(可按需修改,如改为char、float等)
typedef int QDataType;

// 队列结点结构
typedef struct QueueNode
{
QDataType val;// 结点存储的数据
struct QueueNode* next;// 指向下一个结点的指针
} QNode;

// 队列管理结构(记录队头、队尾)

List item

typedef struct Queue
{
QNode* phead;// 队头指针
QNode* ptail;// 队尾指针
} Queue;

二、队列核心接口实现

队列的核心操作包括:初始化、入队、出队、取队头 / 队尾、判空、获取长度、销毁,所有接口的实现均基于链表操作。

1. 队列初始化(QueueInit)

初始化队列的核心是将队头和队尾指针置空,表示空队列。

#include"Queue.h"

// 初始化队列
void QueueInit(Queue* pq) {
assert(pq);// 确保传入的队列指针非空
pq->phead = pq->ptail = NULL;
}

2. 入队列(QueuePush)

入队操作是在队尾插入新结点,需处理两种情况:

  • 队列为空:新结点既是队头也是队尾;
  • 队列非空:将新结点链接到队尾,更新队尾指针。

// 入队列(队尾插入)
void QueuePush(Queue* pq, QDataType x) {
assert(pq);
// 1. 创建新结点
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL) { // 内存分配失败处理
perror(“malloc fail”);
exit(1);
}
newnode->val = x;
newnode->next = NULL;
// 2. 插入到队尾
if (pq->phead == NULL) {
// 空队列
pq->phead = pq->ptail = newnode;
} else {
// 非空队列
pq->ptail->next = newnode;
pq->ptail = newnode;
}
}
3. 队列判空(QueueEmpty)
判空是基础操作,后续出队、取队头 / 队尾都需要先判断队列是否为空,避免访问空指针。

// 队列判空:空返回true,非空返回false
bool QueueEmpty(Queue* pq) {
assert(pq);
return pq->phead == NULL;
}

4. 出队列(QueuePop)
出队操作是删除队头结点,需处理两种情况:

  • 队列只有一个结点:删除后队头、队尾均置空;
  • 队列有多个结点:更新队头指针为下一个结点,释放原队头结点。

// 出队列(队头删除)
void QueuePop(Queue* pq) {
assert(pq);
assert(!QueueEmpty(pq));// 队列非空校验

QNode* del = pq->phead;// 记录要删除的队头结点
if (pq->phead == pq->ptail) {** // 队列只有一个结点**
pq->phead = pq->ptail = NULL;
} else {// 队列有多个结点
pq->phead = pq->phead->next;
}
free(del); // 释放结点内存
del = NULL;
}

5. 取队头 / 队尾数据(QueueFront/QueueBack)

  • 队头数据:直接返回队头结点的val;
  • 队尾数据:直接返回队尾结点的val;
    注意:操作前必须校验队列非空。
    // 取队头数据
    QDataType QueueFront(Queue* pq) {
    assert(pq);
    assert(!QueueEmpty(pq));
    return pq->phead->val;
    }

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

6. 队列销毁(QueueDestroy)
销毁队列需要遍历所有结点,逐个释放内存,最后将队头、队尾指针置空,避免内存泄漏。

// 销毁队列
void QueueDestroy(Queue* pq) {
assert(pq);
QNode* cur = pq->phead;
while (cur) {// 遍历释放所有结点
QNode* del = cur;
cur = cur->next;
free(del);
}
// 重置队头、队尾
pq->phead = pq->ptail = NULL;
}

7. 获取队列长度(QueueSize)
遍历链表,统计结点个数,即为队列的有效元素个数。

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

三、常见错误修正

在初始实现代码中,存在一个典型错误:QueueBack函数错误返回了pq->phead->val(队头数据),而非pq->ptail->val(队尾数据),上述代码已修正该问题。
此外,QueuePop函数中初始代码的断言参数错误(assert(!QueueEmpty(&pq))),正确应为assert(!QueueEmpty(pq))(传入队列指针而非指针的地址)。

四、队列功能测试

编写测试代码,验证队列的核心功能是否正常:

#include"Queue.h"

void test_queue() {
Queue pq;
// 1. 初始化队列
QueueInit(&pq);

// 2. 入队:1 -> 2 -> 3 -> 4
QueuePush(&pq, 1);
QueuePush(&pq, 2);
QueuePush(&pq, 3);
QueuePush(&pq, 4);
printf(“队列长度:%d\n”, QueueSize(&pq));// 输出:4
printf(“队头元素:%d\n”, QueueFront(&pq));// 输出:1
printf(“队尾元素:%d\n”, QueueBack(&pq));// 输出:4

// 3. 出队(删除队头1)
QueuePop(&pq);
printf(“出队后队头:%d\n”, QueueFront(&pq));// 输出:2
printf(“出队后长度:%d\n”, QueueSize(&pq));// 输出:3

// 4. 循环出队,验证先进先出
while (!QueueEmpty(&pq)) {
printf(“出队元素:%d\n”, QueueFront(&pq));
QueuePop(&pq);
}
// 输出:2 -> 3 -> 4

// 5. 销毁队列
QueueDestroy(&pq);
}

int main() {
test_queue();
return 0;
}

五、队列的应用场景

队列的 “先进先出” 特性使其在很多场景中发挥作用:

1. 任务排队:操作系统的任务调度、打印机的打印队列;
2. 广度优先搜索(BFS):二叉树的层序遍历、图的广度优先遍历;
3. 消息队列:分布式系统中实现异步通信(如 RabbitMQ);
4. 缓存策略:FIFO 缓存淘汰算法。

六、总结

本文基于链表实现了队列的核心接口,包括初始化、入队、出队、判空、取队头 / 队尾、销毁、获取长度等。链表实现的队列避免了数组的容量限制,且插入 / 删除操作效率更高。需要注意的是:

  • 操作队列前必须校验指针非空、队列非空;
  • 销毁队列时需遍历释放所有结点,避免内存泄漏;
  • 核心逻辑需遵循 “先进先出” 原则,确保队头删除、队尾插入。
  • 队列作为基础数据结构,是后续学习复杂算法和中间件的重要基础,掌握其实现- 原理和应用场景,对编程能力的提升至关重要。
http://www.jsqmd.com/news/477949/

相关文章:

  • 一、CentOS安装Mysql
  • VSCode 配置 IAR 工程编译、下载与调试指南
  • Matlab语音信号去噪GUI:实现正弦噪声与高斯噪声的滤波处理,巴特沃斯低通与小波变换去噪功能
  • NVMe1.4 Admin Command解析:Format与Identify的LBA格式与安全擦除机制
  • 雷达图像分辨率不够糊成一团?Music算法直接给你整出高清无码!这玩意儿在阵列信号处理里原本用来估计波达方向,但用在雷达成像上简直就是物理外挂
  • MacOS 15+环境下iVerilog与GtkWAVE的集成与实战
  • COMSOL波在可变折射率光纤中的传播
  • Qwen2.5-VL-7B-Instruct部署教程:Ubuntu 22.04 + NVIDIA驱动 + CUDA 12.1兼容配置
  • 彻底卸载OpenClaw(小龙虾)保姆级教程|无残留、保安全
  • 八大排序算法与 Java 代码实现
  • 我用一台 Windows 笔记本,把 OpenClaw 跑起来了(小白可复现)
  • WVP-PRO流媒体服务:无人观看场景下的智能流生命周期管理
  • 研究flow3d模拟选区激光熔化Inconel 718制件内部缺陷的形成机理,优化工艺参数,从...
  • 150+数字人形象免费选!lite-avatar形象库快速部署与使用全攻略
  • Java String 类笔记
  • STM32F103+ESP8266 AP模式实战:TCP/UDP通信与网络调试全流程解析
  • 2.0 ARP欺骗攻击(基础版)
  • CosyVoice2-0.5B声音克隆效果展示:四川话/英文/日文多语种真实案例集
  • 【C++】STL详解(三)—vector使用手册:不看你会后悔
  • Hibernate与JPA方言配置:跨数据库开发的统一接口
  • 分布式事务解决方案全景指南:2PC、TCC、SAGA 与 Seata 实战
  • 【Windows】Dify + Ollama/Xinference/GPUStack:一站式AI开发环境搭建指南
  • 硬件设计之电源反接防护:从基础二极管到高效MOS管的选型实战
  • 跨微服务的“数据孤岛”解法:利用声明式 API 构建去中心化的数据联邦
  • SecGPT-14B步骤详解:Chainlit前端对接vLLM服务全流程
  • 从零到精通:UNIX BENCH性能基准测试全流程实战
  • 深入解析HDMI中的EDID与E-EDID:从基础结构到实际应用
  • StructBERT中文句子相似度WebUI实战手册:Websocket实时结果推送实验
  • 01-SA8155P 冷启动EDL模式硬件配置与常见问题解析
  • 泰山派嵌入式Linux驱动开发基础入门篇