跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C算法

数据结构初阶详解线性表之队列

综述由AI生成队列是只允许在一端插入、另一端删除的线性表,具备先进先出(FIFO)特性。文章基于链表实现队列结构,定义了节点与结构体,涵盖初始化、入队、出队、取头尾元素、统计个数及销毁功能。通过维护头尾指针避免遍历,提升效率,并提供了完整的 C 语言代码示例与交互演示。

古灵精怪发布于 2026/3/16更新于 2026/5/44 浏览
数据结构初阶详解线性表之队列

一、队列的概念

既然我们想要实现先进先出的效果,那肯定就不像栈那样有一端是堵起来的,想必应该是两端都开口吧。嗯,事实确实如此。

**队列:是只允许在一端进行数据的插入操作,在另一端进行数据的删除操作的一种特殊的线性表,其具有先进先出 FIFO(first in first out) 的结构特点。

入队列:进行插入操作的一端叫做队尾

出队列:进行删除操作的一端叫做队头**

下面是队列的示意图:

文章配图

名字叫做队列,其实就像我们排队一样,先排的人先得服务,后排的人后得到服务,在队列中,先进来的元素先得到操作,后进来的服务后得到操作

二、队列的实现

想要实现队列,我们就要考虑与实现栈时一样的问题,那就是:底层用数组实现还是用链表实现?

首先我们来分析一下,队列是需要在队尾和队头进行频繁操作的数据结构,队尾通常容易实现,但是相较于队头,数组要实现所需要的时间成本就要远远大于链表了,所以综上所述,我们决定用链表为底来实现队列

2.1、队列的结构

既然底层用的是链表实现,那么接涉及到节点的问题,我们依旧将队列的节点设置成与链表节点相同即可。但这时候又有一个新的问题:用单链表还是双链表?我们依旧是从队列的定义出发。既然是一端进一端出,那只用考虑头尾位置的节点就行,不会像顺序表或者链表那样涉及到 pos 位置的插入删除,同时兼顾运行效率,我们决定采用单链表的作为队列节点的结构,这样一来,队列节点的结构就成了这样:

//现在定义队列节点
typedef struct QueueNode {
    Elemtype data; //数据域
    struct QueueNode* next; //指针域
}QueueNode;

在队列中,哦不,应该说是在链表中,我们对于头部删除的操作是很方便的,但是队尾部插入的话就要经常遍历一遍链表然后再能进行操。像队列这种经常对队尾进行操作的数据结构,我们总不能每次插入都遍历一遍吧?这样的话真的就比老奶奶过马路还慢了,所以我们要定义一个尾指针,使它一直指向尾结点,所以下面才是队列真正的结构体:

//现在定义队列结构体
typedef struct Queue {
    QueueNode* phead;
    QueueNode* ptail;
}Queue;

2.2、队列的初始化

队列的初始化与其他数据结构一样,都是对自己结构体里面的元素进行初始化,那在队列中,我们是对队列的节点进行初始化呢?还是对队列的头尾指针进行初始化呢?答案是对队列的头尾指针进行初始化,因为队列这个数据结构我们对是对头尾指针进行操作的,对于节点只是一个过程而已:


  {
    pQueue->phead = pQueue->ptail = ;
}
//现在初始化队列
void
Init_Queue
(Queue* pQueue)
NULL

将两个指针置为空就行

2.3、入队

队列的入队一般是在队尾进行操作的,我们在编写代码的时候要留个心眼,如果当前队列一个元素都没有,那在插入的时候就是头尾指针指向同一个节点,如果队列中已经有元素了,那就只用对尾指针进行操作,最后记得将尾指针指向最新的节点下面是具体代码:

//现在是入队列
void Push_Queue(Queue* pQueue, Elemtype data) {
    assert(pQueue);
    //创建新节点
    QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
    if (newNode == NULL) {
        printf("新节点创建失败....\n");
        return;
    }
    newNode->data = data;
    newNode->next = NULL;
    if (pQueue->phead == NULL) {
        //说明是头节点
        pQueue->phead = pQueue->ptail = newNode;
    } else {
        pQueue->ptail->next = newNode;
    }
    pQueue->ptail = newNode;
}

2.4、出队

出队一般来说是对头指针进行操作的,与入队一样,我们也考虑到当队列中只有一个元素时,释放完头指针,要将头指针与尾指针同时置为 NULL,如果队列中不止一个元素,那就只用对头指针进行操作,然后将头指针指向下一个元素,下面是具体的代码:

//出队列
void Pop_Queue(Queue* pQueue) {
    assert(pQueue);
    if (pQueue->phead == NULL) {
        printf("当前队列为空,无法出队...\n");
        return;
    }
    //如果只有一个节点
    if (pQueue->phead == pQueue->ptail) {
        free(pQueue->phead);
        pQueue->phead = pQueue->ptail = NULL;
    } else {
        QueueNode* delet = pQueue->phead;
        pQueue->phead = pQueue->phead->next;
        free(delet);
        delet = NULL;
    }
}

2.5、取队头队尾数据

这个操作相对简单一点,我们只用返回队头队尾的节点元素即可,代码如下:

//取队头元素
Elemtype Get_first_elem(Queue* pQueue) {
    assert(pQueue);
    return pQueue->phead->data;
}

//取队尾元素
Elemtype Get_tail_elem(Queue* pQueue) {
    assert(pQueue);
    return pQueue->ptail->data;
}

这里要注意的是,我们在设计返回类型的时候,要将返回值设置为我们想要的类型,在这里我提前将 int 类型重命名为了 Elemtype,在代码总和那一块我会展示出来。

2.6、队列中有效的元素个数

在这个操作中,我们有两种操作方式,第一种就是传统的使用遍历的方式,将节点数一一记录下来;第二种就是在入队的时候用一个变量 size++,在出队的时候对 size--,这样就能实时更新队列中的元素个数了,第二种方式在返回个数的时候要比第一种快很多,下面是两种代码的展示:

//第一种的传统方式://计算队列中有效的元素个数
int Size_Queue(Queue* pQueue) {
    assert(pQueue);
    int size = 0;
    QueueNode* pcur = pQueue->phead;
    while (pcur) {
        size++;
        pcur = pcur->next;
    }
    return size;
}

//第二种返回方式(需维护全局或成员变量 size)
// int Size_Queue(Queue* pQueue, int size){
//     assert(pQueue);
//     return size;
// }

2.7、队列的销毁

队列的销毁与链表的销毁相同,用遍历指针逐一将节点一一销毁,最后将头尾指针置为 NULL:

void Destory_Queue(Queue* pQueue) {
    assert(pQueue);
    QueueNode* pcur = pQueue->phead;
    while (pcur) {
        QueueNode* delet = pcur;
        pcur = pcur->next;
        free(delet);
        delet = NULL;
    }
    pQueue->phead = pQueue->ptail = NULL;
}

要提醒的是,一定要将头尾指针置为 NULL,不要这两个指针会成为野指针!!!!

三、综合代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<windows.h>

typedef int Elemtype;

//现在定义队列节点:
typedef struct QueueNode {
    Elemtype data; //数据域
    struct QueueNode* next; //指针域
}QueueNode;

//现在定义队列结构体:
typedef struct Queue {
    QueueNode* phead;
    QueueNode* ptail;
}Queue;

//现在初始化队列:
void Init_Queue(Queue* pQueue) {
    pQueue->phead = pQueue->ptail = NULL;
}

//现在是入队列
void Push_Queue(Queue* pQueue, Elemtype data) {
    assert(pQueue);
    //创建新节点
    QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
    if (newNode == NULL) {
        printf("新节点创建失败....\n");
        return;
    }
    newNode->data = data;
    newNode->next = NULL;
    if (pQueue->phead == NULL) {
        //说明是头节点
        pQueue->phead = pQueue->ptail = newNode;
    } else {
        pQueue->ptail->next = newNode;
    }
    pQueue->ptail = newNode;
}

//出队列
void Pop_Queue(Queue* pQueue) {
    assert(pQueue);
    if (pQueue->phead == NULL) {
        printf("当前队列为空,无法出队...\n");
        return;
    }
    //如果只有一个节点
    if (pQueue->phead == pQueue->ptail) {
        free(pQueue->phead);
        pQueue->phead = pQueue->ptail = NULL;
    } else {
        QueueNode* delet = pQueue->phead;
        pQueue->phead = pQueue->phead->next;
        free(delet);
        delet = NULL;
    }
}

//取队头元素
Elemtype Get_first_elem(Queue* pQueue) {
    assert(pQueue);
    return pQueue->phead->data;
}

//取队尾元素
Elemtype Get_tail_elem(Queue* pQueue) {
    assert(pQueue);
    return pQueue->ptail->data;
}

//计算队列中有效的元素个数
int Size_Queue(Queue* pQueue) {
    assert(pQueue);
    int size = 0;
    QueueNode* pcur = pQueue->phead;
    while (pcur) {
        size++;
        pcur = pcur->next;
    }
    return size;
}

void Destory_Queue(Queue* pQueue) {
    assert(pQueue);
    QueueNode* pcur = pQueue->phead;
    while (pcur) {
        QueueNode* delet = pcur;
        pcur = pcur->next;
        free(delet);
        delet = NULL;
    }
    pQueue->phead = pQueue->ptail = NULL;
}

//现在是打印队列
void my_printf(Queue* pQueue) {
    assert(pQueue);
    if (pQueue->phead == NULL) {
        printf("当前队列为空,无法打印...\n");
        return;
    } else {
        QueueNode* pcur = pQueue->phead;
        while (pcur) {
            printf("%d ", pcur->data);
            pcur = pcur->next;
        }
    }
}

//现在是打印菜单
void print_menu() {
    printf("=========================================\n");
    printf("1.入队 2.出队\n");
    printf("3.取队头元素 4.取队尾元素\n");
    printf("5.计算队列元素个数\n");
    printf("=========================================\n");
    printf("\n");
}

int main() {
    Queue L;
    Queue* pQueue = &L;
    Init_Queue(pQueue);
    int choose;
    do {
        system("cls");
        print_menu();
        printf("当前的队列为:\n");
        my_printf(pQueue);
        printf("\n");
        printf("请输入你的选择:\n");
        scanf("%d", &choose);
        switch (choose) {
        case 1: {
            printf("请输入你想输入的元素个数:\n");
            int num = 0;
            scanf("%d", &num);
            Elemtype data = 0;
            printf("请输入你想输入的元素:\n");
            for (int i = 0; i < num; i++) {
                scanf("%d", &data);
                Push_Queue(pQueue, data);
            }
            printf("正在输入...\n");
            Sleep(1000);
            printf("输入成功~\n");
            Sleep(2000);
            break;
        }
        case 2: {
            printf("正在出队...\n");
            Sleep(1000);
            Pop_Queue(pQueue);
            printf("出队成功~\n");
            Sleep(2000);
            break;
        }
        case 3: {
            Elemtype elem = Get_first_elem(pQueue);
            printf("正在取出对队头元素...\n");
            Sleep(1000);
            printf("队头元素为:%d", elem);
            Sleep(2000);
            break;
        }
        case 4: {
            Elemtype elem = Get_tail_elem(pQueue);
            printf("正在取出对队尾元素...\n");
            Sleep(1000);
            printf("队尾元素为:%d", elem);
            Sleep(2000);
            break;
        }
        case 5: {
            printf("正在计算队列大小...\n");
            int num = Size_Queue(pQueue);
            Sleep(1000);
            printf("队列的大小为:%d", num);
            Sleep(1000);
            break;
        }
        case -1: {
            printf("正在退出程序...\n");
            Sleep(1000);
            printf("退出成功~\n");
            Sleep(500);
        }
        }
    } while (choose != -1);
    Destory_Queue(pQueue);
    return 0;
}

目录

  1. 一、队列的概念
  2. 二、队列的实现
  3. 2.1、队列的结构
  4. 2.2、队列的初始化
  5. 2.3、入队
  6. 2.4、出队
  7. 2.5、取队头队尾数据
  8. 2.6、队列中有效的元素个数
  9. 2.7、队列的销毁
  10. 三、综合代码
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 主流 Python IDE 优缺点对比与选型指南
  • LLaMA 大模型微调实践与心得:基于 LoRA 的高效方案
  • Linux 线程概念详解
  • Llama-2-7b 在昇腾 NPU 上的六大核心场景性能基准
  • DeepSeek 驱动下的前端开发效能提升与实战指南
  • DeepSeek 时代:前端开发的变革与实战指南
  • 大模型选型避坑指南:AI Ping 性能评测与实战建议
  • Python-Binance 库:Binance 平台 API 集成开发指南
  • 滑动窗口算法实战:从入门到经典题型解析
  • 开源智能家居平台指南:构建自主可控的智能生活系统
  • HarmonyOS6 RcButton 组件交互逻辑与事件处理机制
  • OpenClaw 多端交互实测指南:Web、TUI 与钉钉集成配置
  • 用 Rust 构建 Git 提交历史可视化工具
  • 滑动窗口算法详解:最小子数组与无重复字符
  • OpenClaw 技术解构:从 WhatsApp 机器人到本地 AI 系统
  • DBeaver 安装与 MySQL 数据库连接配置
  • Hexo 博客常用命令速查指南
  • AR 健身教练实践:基于 Rokid CXR-M SDK 的落地实现
  • IDEA 集成 AI 辅助工具推荐
  • Python 学习入门路径与主流应用方向详解

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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