跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

数据结构:队列详解与实现

数据结构中队列的概念,包括其先进先出(FIFO)特性、队头与队尾的定义。文章通过类比餐厅排队帮助理解队列逻辑。重点讲解了使用链表结构实现队列的原因及具体思路,提供了完整的 C 语言代码示例,涵盖初始化、入队、出队、获取元素、销毁等核心操作函数,并附带测试用例,适合学习线性表进阶知识。

樱花落尽发布于 2026/3/26更新于 2026/9/105.7K 浏览
数据结构:队列详解与实现

数据结构:队列详解与实现

一、队列是什么?

1. 队列的定义

队列: 特殊的线性表,只允许在一端进行插入数据操作,在另一端进行删除数据操作的一种数据结构。 队列具有先进先出(FIFO, First In First Out)的原则。

2. 队尾&&队头

进行插入操作的一端称为队尾 进行删除操作的一端称为队头

队列示意图

3. 类比

就像我们在餐厅吃饭时,若饭店满人了,也许就会开始排队摇号,有 1 号、2 号、3 号…等等。 当有座位空出来时,就让序号前的人先去;当又有人来时,就在队尾加一个序列。 而队列也是如此,只允许在一端进行插入数据操作,在另一端进行删除数据操作的一种数据结构。 当要删除数据时,就让队头的数据(先来的数据)先删除;当要插入数据时,就在队尾插入数据。

二、队列的实现

那么,我们该如何来实现队列呢?

1. 用什么来实现?

想要实现队列,首先要想清楚要用什么实现队列。 之前,我们依次实现了顺序表、链表以及栈,他们都是用数组或链表来实现的。

队列也可以数组和链表的结构实现。 但是之前我们讲到了在数组上头删(出队列)很麻烦,后续的每一个元素都要往前挪一位。 如果使用数组的结构来实现队列,出队列就会在数组头上出数据,效率会很低。 综上所诉,使用链表的结构来实现队列更优一些。

2. 实现思路

由于队列与单链表类似,这里可以拿单链表的实现来做参考

  • 第 1 步

    与单链表同理,队列中的每一个节点有以下两成员:

    1. 指向下一个节点的指针
    2. 节点中存储的数据
  • 第 2 步 但是,我们需要记录下队列的总元素个数、头节点、尾节点这三个重要信息。 于是乎,我们可以单独创建一个结构体来包含这些信息,将队列的总元素个数、头节点、尾节点包含在内。

  • 第 3 步 最后,就是一一实现我们队列的增、删、查、改等操作了。

3. 代码实现

本文以创建一个 int 类型的队列为例

(1)创建头文件&源文件

详见扫雷游戏等复杂程序的开发规范

如图: 创建了一个 Queue.h 头文件,用于存放用来放函数的声明和一些库函数的头文件。 创建了一个 Queue.c 源文件,用于用来放函数的定义 (队列的主体)。 还有一个 Test.c 源文件,用于测试实现的队列的运行效果。

(2)定义队列(定义)

首先我们要定义一个队列。 这里之前讲过,要先创建一个结构体来表示每个节点,再单独创建一个结构体来包含这些信息,将队列的总元素个数、头节点、尾节点包含在内。 这样方便我们对队列的实现。

代码演示:(内有注释) (在头文件 Queue.h 中写)

//重定义,方便修改类型
typedef int QDataType; //表示每个节点的类型

typedef struct QListNode {
    QDataType data;      //节点中存储的数据
    struct QListNode* next; //指向下一个节点的指针
} QNode;

// 队列的结构
typedef struct Queue {
    QNode* front; //头指针
    QNode* tail;  //尾指针
    int size;     //总元素个数
} Queue;

在定义队列的代码中,有两个需要注意的点: 本文是以 int 类型为例,但如果以后要将队列中的元素类型修改成 char 类型或是其他类型一个一个修改就很麻烦。 所以我们重定义 int 类型为 QDataType,并将接下来代码中的 int 类型全部写成 QDataType。 这是为了方便我们以后对类型进行修改,仅需将 int 改为其他类型即可。 在定义队列的同时重定义队列的变量名为 QNode 方便以后使用。

(3)队列的初始化(初始化)

定义完队列后,肯定要对队列进行初始化,内容全部置 0 / NULL。

代码演示:(内有注释) (其中 q 是一个指向队列的指针,下同)

在 Queue.h 头文件中写到:

// 初始化队列
void QueueInit(Queue* q);

在 Queue.c 源文件中写到:

// 初始化队列
void QueueInit(Queue* q) {
    assert(q); //断言空指针
    q->front = NULL;
    q->tail = NULL;
    q->size = 0; //全部初始化置 0 / NULL
}

在写实现代码中,有一个很重要的点: 当我们函数在进行传参时,可能会传入空指针,而我们知道空指针是不能进行解引用的。 故为了我们的代码更加健壮,可以加入 assert 断言 来判断是否符合条件,在之后的代码中也都有。

(4)队列的销毁(销毁)

在我们的程序运行完毕后,当然要对队列进行销毁,以免占用内存。 这里我们直接遍历链表,一一释放空间销毁就行啦。

代码演示:(内有注释) (其中 q 是一个指向队列的指针,下同)

在 Queue.h 头文件中写到:

// 销毁队列
void QueueDestroy(Queue* q);

在 Queue.c 源文件中写到:

// 销毁队列
void QueueDestroy(Queue* q) {
    assert(q); //断言空指针
    QNode* cur = q->front; //遍历链表,一一释放空间销毁
    while(cur) {
        QNode* next = cur->next;
        free(cur);
        cur = next;
    }
    q->front = q->tail = NULL;
    q->size = 0; //全部初始化置 0 / NULL
}
(5)队尾入队列(尾插)
  • 怎么插入?

    在入队列时,由于队列只允许在一端进行插入数据操作,在另一端进行删除数据。 所以我们在插入时进行尾插数据,而在删除时进行头删数据。

  • 怎么开辟空间?

    由于这里是链表的结构,故每一次插入时开辟一个节点的空间就行了。

  • 注意!!!

    注意:当队列中没有节点时,此时插入的节点既是头节点,又是尾节点,所以头尾节点都要赋值。

代码演示:(内有注释) (其中 q 是一个指向队列的指针,下同)

在 Queue.h 头文件中写到:

// 队尾入队列
void QueuePush(Queue* q, QDataType data);

在 Queue.c 源文件中写到:

// 队尾入队列
void QueuePush(Queue* q, QDataType data) {
    assert(q); //断言空指针
    QNode* tmp = (QNode*)malloc(sizeof(QNode)); //直接开辟一个节点的空间
    if(tmp == NULL) { //加一个 if语句 防止增容失败
        perror("malloc");
        return;
    }
    //没有问题后就赋值
    tmp->data = data;
    tmp->next = NULL;
    //注意:当队列中没有节点时
    //此时插入的节点既是头节点,又是尾节点
    if(q->size == 0) {
        q->front = tmp;
        q->tail = tmp;
    } else {
        q->tail->next = tmp;
        q->tail = tmp;
    }
    q->size++;
}
(6)队头出队列(头删)
  • 怎么删除?

    在出队列时,由于队列只允许在一端进行插入数据操作,在另一端进行删除数据。 刚刚我们在队尾进行插入数据,而现在删除数据时就是用头删数据。

  • 注意!!!

    注意:当队列中只有一个节点时,此时头尾节点相等,都要进行删除,所以将头节点和尾节点都释放。

代码演示:(内有注释) (其中 q 是一个指向队列的指针,下同)

在 Queue.h 头文件中写到:

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

在 Queue.c 源文件中写到:

// 队头出队列
void QueuePop(Queue* q) {
    assert(q);
    assert(q->size > 0); //断言空指针
                         //断言顺序表不能为空
    //注意:当队列中只有一个节点时,头尾节点相等
    //此时将头节点和为尾节点都释放
    if(q->size == 1) {
        free(q->front);
        q->front = NULL;
        q->tail = NULL;
    } else {
        QNode* del = q->front;
        q->front = q->front->next;
        free(del);
    }
    q->size--;
}
(7)获取队列头部元素

这个很简单,直接用下标进行访问数据,再返回所对应的值。

代码演示:(内有注释) (其中 q 是一个指向队列的指针,下同)

在 Queue.h 头文件中写到:

// 获取队列头部元素
QDataType QueueFront(Queue* q);

在 Queue.c 源文件中写到:

// 获取队列头部元素
QDataType QueueFront(Queue* q) {
    assert(q);
    assert(q->size > 0); //断言空指针
                         //断言顺序表不能为空
    return q->front->data;
}
(8)获取队列尾部元素

这个很简单,直接用下标进行访问数据,再返回所对应的值。

代码演示:(内有注释) (其中 q 是一个指向队列的指针,下同)

在 Queue.h 头文件中写到:

// 获取队列队尾元素
QDataType QueueBack(Queue* q);

在 Queue.c 源文件中写到:

// 获取队列队尾元素
QDataType QueueBack(Queue* q) {
    assert(q);
    assert(q->size > 0); //断言空指针
                         //断言顺序表不能为空
    return q->tail->data;
}
(9)获取队列中有效元素个数

这个很简单,直接返回所对应的值。

代码演示:(内有注释) (其中 q 是一个指向队列的指针,下同)

在 Queue.h 头文件中写到:

// 获取队列中有效元素个数
int QueueSize(Queue* q);

在 Queue.c 源文件中写到:

// 获取队列中有效元素个数
int QueueSize(Queue* q) {
    assert(q); //断言空指针
    return q->size;
}
(10)检测队列是否为空

这个很简单,如果队列为空返回非零结果,如果不为空返回 0。

代码演示:(内有注释) (其中 q 是一个指向队列的指针,下同)

在 Queue.h 头文件中写到:

// 检测队列是否为空,如果为空返回非零结果,如果不为空返回 0
int QueueEmpty(Queue* q);

在 Queue.c 源文件中写到:

// 检测队列是否为空,如果为空返回非零结果,如果非空返回 0
int QueueEmpty(Queue* q) {
    assert(q); //断言空指针
    return q->size == 0;
}

三、完整代码实现

1. Queue.h

用于存放用来放函数的声明和一些库函数的头文件

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

//重定义,方便修改类型
typedef int QDataType; //表示每个节点的类型

typedef struct QListNode {
    QDataType data;      //节点中存储的数据
    struct QListNode* next; //指向下一个节点的指针
} QNode;

// 队列的结构
typedef struct Queue {
    QNode* front; //头指针
    QNode* tail;  //尾指针
    int size;     //总元素个数
} Queue;

// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回 0
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
2. Queue.c

用于用来放函数的定义 (队列的主体)

#include "Queue.h"

// 初始化队列
void QueueInit(Queue* q) {
    assert(q); //断言空指针
    q->front = NULL;
    q->tail = NULL;
    q->size = 0; //全部初始化置 0 / NULL
}

// 队尾入队列
void QueuePush(Queue* q, QDataType data) {
    assert(q); //断言空指针
    QNode* tmp = (QNode*)malloc(sizeof(QNode)); //直接开辟一个节点的空间
    if(tmp == NULL) { //加一个 if语句 防止增容失败
        perror("malloc");
        return;
    }
    //没有问题后就赋值
    tmp->data = data;
    tmp->next = NULL;
    //注意:当队列中没有节点时
    //此时插入的节点既是头节点,又是尾节点
    if(q->size == 0) {
        q->front = tmp;
        q->tail = tmp;
    } else {
        q->tail->next = tmp;
        q->tail = tmp;
    }
    q->size++;
}

// 队头出队列
void QueuePop(Queue* q) {
    assert(q);
    assert(q->size > 0); //断言空指针
                         //断言顺序表不能为空
    //注意:当队列中只有一个节点时,头尾节点相等
    //此时将头节点和为尾节点都释放
    if(q->size == 1) {
        free(q->front);
        q->front = NULL;
        q->tail = NULL;
    } else {
        QNode* del = q->front;
        q->front = q->front->next;
        free(del);
    }
    q->size--;
}

// 获取队列头部元素
QDataType QueueFront(Queue* q) {
    assert(q);
    assert(q->size > 0); //断言空指针
                         //断言顺序表不能为空
    return q->front->data;
}

// 获取队列队尾元素
QDataType QueueBack(Queue* q) {
    assert(q);
    assert(q->size > 0); //断言空指针
                         //断言顺序表不能为空
    return q->tail->data;
}

// 获取队列中有效元素个数
int QueueSize(Queue* q) {
    assert(q); //断言空指针
    return q->size;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回 0
int QueueEmpty(Queue* q) {
    assert(q); //断言空指针
    return q->size == 0;
}

// 销毁队列
void QueueDestroy(Queue* q) {
    assert(q); //断言空指针
    QNode* cur = q->front; //遍历链表,一一释放空间销毁
    while(cur) {
        QNode* next = cur->next;
        free(cur);
        cur = next;
    }
    q->front = q->tail = NULL;
    q->size = 0; //全部初始化置 0 / NULL
}
3. Test.c

用于测试实现的队列的运行效果

#include "Queue.h"

int main() {
    Queue Q;
    QueueInit(&Q);
    QueuePush(&Q, 1);
    QueuePush(&Q, 2);
    QueuePush(&Q, 3);
    QueuePush(&Q, 4);
    QueuePush(&Q, 5);
    QueuePush(&Q, 6);
    /*QueuePop(&Q); 
     int ret1 = QueueFront(&Q); 
     QueuePop(&Q); 
     int ret2 = QueueFront(&Q); 
     QueuePop(&Q); 
     int ret3 = QueueFront(&Q); 
     printf("%d %d %d\n\n", ret1, ret2, ret3);*/
    while(QueueEmpty(&Q)) {
        printf("%d ", QueueFront(&Q));
        QueuePop(&Q);
    }
    printf("\n\n");
    QueueDestroy(&Q);
    return 0;
}

参考资料

  • C++ Reference

目录

  1. 数据结构:队列详解与实现
  2. 一、队列是什么?
  3. 1. 队列的定义
  4. 2. 队尾&&队头
  5. 3. 类比
  6. 二、队列的实现
  7. 1. 用什么来实现?
  8. 2. 实现思路
  9. 3. 代码实现
  10. (1)创建头文件&源文件
  11. (2)定义队列(定义)
  12. (3)队列的初始化(初始化)
  13. (4)队列的销毁(销毁)
  14. (5)队尾入队列(尾插)
  15. (6)队头出队列(头删)
  16. (7)获取队列头部元素
  17. (8)获取队列尾部元素
  18. (9)获取队列中有效元素个数
  19. (10)检测队列是否为空
  20. 三、完整代码实现
  21. 1. Queue.h
  22. 2. Queue.c
  23. 3. Test.c
  24. 参考资料

更多推荐文章

查看全部
  • Git Reset 核心原理与协作边界指南
  • 国产 AI Agent 工具全面对比及使用指南
  • 智能桌面机器人 DIY 实战:构建专属 AI 伙伴
  • Android 基于 WebRTC 的高清语音通话实现与性能优化
  • 高效集成 Gemini API:Zotero 学术场景 AI 辅助分析指南
  • 2025 上海市人工智能训练师高级/三级考试介绍与复习
  • VS Code 远程连接后 GitHub Copilot 无法使用怎么办
  • 循环神经网络(RNN)与 LSTM 文本分类实战
  • DeepSeek 各版本说明与优缺点分析
  • node-llama-cpp 常见错误处理与调试方法
  • 国内升级 GitHub Copilot 专业版:PayPal 支付方案详解
  • SpringBoot 接入 JaCoCo 实践:代码覆盖率统计
  • Docker 部署 OpenClaw 本地 AI 执行引擎(智谱 AI 版)
  • TRAE SOLO 远程开发体验与 cpolar 内网穿透方案
  • C++ STL 详解:从零实现 vector 容器
  • RV1106 基于 libdatachannel 的 4G WebRTC 实时视频传输方案
  • Whisper Large v3 模型解析:语音特征提取与处理机制
  • physipy: 使 Python 具有单位感知
  • Web 服务与 I/O 模型
  • Java ArrayList 底层原理与核心方法手写实现

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online