C语言数据结构——详细讲解《队列》

C语言数据结构——详细讲解《队列》

  • 前言
  • 一、队列的概念
  • 二、队列的操作
    • (一)定义队列结构
    • (二)初始化队列
    • (三)入队列操作
    • (四)出队列操作
    • (五)获取队头元素
    • (六)检查队列是否为空
    • (七)销毁队列
  • (三)本文所有代码
    • 1.Queue.h文件
    • 2.Queue.c文件


前言

  • 在上一篇博客中,我们详细探讨了栈这种数据结构,今天,让我们深入了解另一种与栈类似重要的数据结构 —— 队列

一、队列的概念

  • 队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列遵循先进先出的原则
  • 入队列:进行插入操作的一端称为队尾
  • 出队列:进行删除操作的一端称为队头

在这里插入图片描述

  • 大家思考一个问题,队列的底层结构是数组还是链表
队列的底层结构既可以是数组,也可以是链表
  • 基于数组实现的队列
  • 优点

实现简单,逻辑清晰;可直接通过索引随机访问元素,时间复杂度为 O (1)。

  • 缺点

需预先确定队列大小。若队列元素超数组大小,扩容操作复杂且耗时

  • 基于链表实现的队列
  • 优点

可动态调整大小,无需预先确定队列长度;队头和队尾的插入、删除操作时间复杂度为 O (1)

  • 缺点

每个节点需额外存储指针,占用空间。
无法直接通过索引访问元素,随机访问效率低

今天我们来详细讲解一下基于链表实现的队列

在这里插入图片描述

二、队列的操作

(一)定义队列结构

  • 首先我们需要定义队列链表节点结构体
//定义队列结点的结构
typedef struct QueueNode
{
    QDataType data;
    struct QueueNode* next;
}QueueNode;
  • 队列结点结构中的data成员用于存储队列中的实际数据
  • 接下来,我们定义队列结构体本身
//定义队列的结构
typedef struct Queue
{
    QueueNode* phead;  //队头
    QueueNode* ptail;  //队尾
}Queue;
  • 接下来需要初始化队列
// 初始化队列
void initQueue(Queue *pq) {
    pq->phead = NULL;
    pq->ptail = NULL;
}

(二)初始化队列

  • 在使用队列之前,我们需要对其进行初始化操作,将队头和队尾指针都设置为 NULL,表示队列为空。
// 初始化队列
void initQueue(Queue *pq) 
{
    pq->phead = NULL;
    pq->ptail = NULL;
}

(三)入队列操作

// 入队操作
void enqueue(Queue *pq, int x) {
    Node *newNode = (Node *)malloc(sizeof(Node));
    newNode->data = x;
    newNode->next = NULL;

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

  • 创建一个新节点,给它分配内存空间,
  • 把要入队的元素值赋给新节点的 data,并把 next 设为 NULL。
  • 然后看队列是不是空的,如果空,新节点就既是队头又是队尾;
  • 如果不空,就把新节点接到队尾节点后面,并更新队尾指针指向新节点

(四)出队列操作


//出队列
int dequeue(Queue* pq)
{
    assert(pq->phead != NULL);
    Node* temp = pq->phead;
    int x = temp->data;
    if (pq->phead == pq->ptail)
    {
        pq->phead = NULL;
        pq->ptail = NULL;
    }
    else
    {
        pq->phead = pq->phead->next;
    }
    free(temp);
    return x;
}
  • 从队列的队头取出一个元素并删掉对应的节点。
  • 检查队列不能是空的,然后把队头节点的数据存起来,
  • 接着看队列是不是只有一个元素
  • 如果是就把队头和队尾指针都设为 NULL;
  • 如果不止一个元素,就更新队头指针指向下一个节点
  • 最后释放原来队头节点的内存并返回取出的元素值。

(五)获取队头元素


// 获取队头元素
int front(Queue* pq)
{
    assert(pq->phead != NULL);
    return pq->phead->data;
}
  • 能获取队列的队头元素的值,但不把这个元素从队列里取出来。
  • 先得确保队列不是空的,然后直接返回队头节点的 data 值就行

(六)检查队列是否为空

int isEmpty(Queue *pq) 
{
    return pq->front == NULL;
}

  • 如果是就说明队列为空返回相应结果,这样在其他操作前可以先检查下队列状态,避免在空队列上做无效操作

(七)销毁队列

// 释放队列内存
void freeQueue(Queue* pq)
{
    Node* current = pq->phead;
    Node* next;
    while (current != NULL)
    {
        next = current->next;
        free(current);
        current = next;
    }
    pq->phead = NULL;
    pq->ptail = NULL;
}

  • 在队列用完后用来释放它占用的内存资源。
  • 从队头开始,逐个遍历队列里的节点,释放每个节点的内存,
  • 最后把队头和队尾指针都设为 NULL,让队列回到初始的空状态

(三)本文所有代码

1.Queue.h文件

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

// 链表节点结构体
typedef struct Node {
    int data;
    struct Node* next;
} Node;

// 队列结构体
typedef struct Queue {
    Node* phead;
    Node* ptail;
} Queue;

// 初始化队列
void initQueue(Queue* pq) {
    pq->phead = NULL;
    pq->ptail = NULL;
}

//入队列
void enqueue(Queue* pq, int x)
{
    Node* newnode = (Node*)malloc(sizeof(Node));
    newnode->data = x;
    newnode->next = NULL;
    if (pq == NULL)
    {
        pq->phead = newnode;
        pq->ptail = newnode;
    }
    else
    {
        pq->ptail->next = newnode;
        pq->ptail = newnode;
    }
}


//出队列
int dequeue(Queue* pq)
{
    assert(pq->phead != NULL);
    Node* temp = pq->phead;
    int x = temp->data;
    if (pq->phead == pq->ptail)
    {
        pq->phead = NULL;
        pq->ptail = NULL;
    }
    else
    {
        pq->phead = pq->phead->next;
    }
    free(temp);
    return x;
}

// 获取队头元素
int front(Queue* pq)
{
    assert(pq->phead != NULL);
    return pq->phead->data;
}


// 检查队列是否为空
int isEmpty(Queue* pq) 
{
    return (pq->phead != NULL) ? 0 : 1;
}



// 释放队列内存
void freeQueue(Queue* pq)
{
    Node* current = pq->phead;
    Node* next;
    while (current != NULL)
    {
        next = current->next;
        free(current);
        current = next;
    }
    pq->phead = NULL;
    pq->ptail = NULL;
}

2.Queue.c文件

#include "Queue.h"
#include <stdio.h>

int main() {
    Queue myQueue;
    initQueue(&myQueue);

    // 先输入一串数据来初始化队列
    int num;
    printf("请输入一串整数,以空格分隔,用于初始化队列,输入非整数结束输入:\n");

    // 逐个读取输入的数据并将其入队
    while (scanf_s("%d", &num) == 1) {
        enqueue(&myQueue, num);
    }

    // 清除输入缓冲区中的剩余字符(例如换行符等)
    while (getchar() != '\n');

    int choice, value;

    do {
        printf("请选择操作:\n");
        printf("1. 入队\n");
        printf("2. 出队\n");
        printf("3. 获取队头元素\n");
        printf("4. 检查队列是否为空\n");
        printf("5. 退出\n");
        scanf_s("%d", &choice);

        switch (choice) {
        case 1:
            printf("请输入要入队的值:");
            scanf_s("%d", &value);
            enqueue(&myQueue, value);
            break;
        case 2:
            if (!isEmpty(&myQueue)) {
                printf("出队元素: %d\n", dequeue(&myQueue));
            }
            else {
                printf("队列为空,无法出队\n");
            }
            break;
        case 3:
            if (!isEmpty(&myQueue)) {
                printf("当前队头元素: %d\n", front(&myQueue));
            }
            else {
                printf("队列为空,无队头元素\n");
            }
            break;
        case 4:
            if (isEmpty(&myQueue)) {
                printf("队列为空\n");
            }
            else {
                printf("队列不为空\n");
            }
            break;
        case 5:
            printf("退出程序\n");
            break;
        default:
            printf("无效的选择,请重新输入\n");
        }

    } while (choice != 5);

    // 释放队列内存
    freeQueue(&myQueue);

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/925669.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【模块一】kubernetes容器编排进阶业务容器化案例

Kubernetes 实战案例 Kubernetes实战案例-规划(基于nerdctl buildkitdcontainerd构建容器镜像) 业务容器化优势&#xff1a; ① 提高资源利用率、节约部署IT成本。 ② 提高部署效率&#xff0c;基于kubernetes实现微服务的快速部署与交付、容器的批量调度与秒级启动。 ③…

政安晨【零基础玩转各类开源AI项目】探索Cursor-AI Coder的应用实例

目录 Cusor的主要特点 Cusor实操 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; Cursor 是 Visual Studio Code 的一个分支。这使我们能够…

Android 12.0 DocumentsUI文件管理器首次进入默认显示内部存储文件功能实现

1.前言 在12.0的系统rom定制化开发中,在关于文件管理器的某些功能中,在首次进入文件管理器的时候默认进入下载 文件夹,点击菜单选择内部存储的时候,会显示内部存储的内容,客户开发需要要求默认显示内部存储的文件 接下来分析下功能的实现 如图: 2.DocumentsUI文件管理器首…

9.机器学习--SVM支持向量机

支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;是一种二分类监督学习模型。支持向量机最早在 1964 年被提出&#xff0c;1995年前后理论成熟并开始被大量应用与人像识别、文本分类等问题中。它的基本模型是定义在特征空间上的间隔最大的线性分类器&…

数据结构---链表

1. 简介 链表&#xff08;Linked List&#xff09;是一种常见的线性数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含数据部分和指向下一个节点的指针&#xff08;或引用&#xff09;。链表的一个主要优点是能够高效地插入和删除元素&#xff0c;尤其是在数组…

“移门缓冲支架:为家庭安全加码”

在智能家居日益普及的今天&#xff0c;科技不仅改变了我们的生活方式&#xff0c;也提升了家居的安全。移门缓冲支架作为一项结合了现代技术的小型装置&#xff0c;正逐渐成为提升家庭安全的重要配件。它通过吸收门关闭时的冲击力、减缓关门速度以及减少噪音等多重功能&#xf…

vscode、android studio、vim 国产AI编程插件Fitten Code

文章目录 Fitten Code简介vim安装Fitten Code插件Android Studio安装Fitten Code插件Fitten Code功能相关文章 Fitten Code简介 Fitten Code是由非十大模型驱动的AI编程助手&#xff0c;它可以自动生成代码&#xff0c;提升开发效率&#xff0c;帮您调试Bug&#xff0c;节省您…

一个月速成python+OpenCV图像处理

OpenCV是一个广受欢迎且极为流行的计算机视觉库&#xff0c;它因其强大的功能、灵活性和开源特性而在开发者和研究者中备受青睐。 学习OpenCV主要就是学习里面的计算机视觉算法。要学习这些算法的原理&#xff0c;知道它们适用于哪些场景&#xff0c;然后通过Python编写代码来…

深度学习2:从零开始掌握PyTorch:数据操作不再是难题

文章目录 一、导读二、张量的定义与基本操作三、广播机制四、索引与切片五、内存管理六、与其他Python对象的转换本文是经过严格查阅相关权威文献和资料,形成的专业的可靠的内容。全文数据都有据可依,可回溯。特别申明:数据和资料已获得授权。本文内容,不涉及任何偏颇观点,…

win10系统安装docker-desktop

1、开启Hyper-v ———————————————— Hyper-V 是微软提供的一种虚拟化技术&#xff0c;它允许你在同一台物理计算机上运行多个独立的操作系统实例。这种技术主要用于开发、测试、以及服务器虚拟化等领域。 —————————————————————— &#…

如何使用谷歌浏览器访问被屏蔽的网站

在互联网浏览过程中&#xff0c;我们有时会遇到一些网站被屏蔽的情况&#xff0c;这可能是因为地域限制、网络审查或其他原因。对于使用谷歌浏览器的用户来说&#xff0c;有几种方法可以尝试访问这些被屏蔽的网站。本文将详细介绍如何使用谷歌浏览器访问被屏蔽的网站。&#xf…

Next.js -服务端组件如何渲染

#题引&#xff1a;我认为跟着官方文档学习不会走歪路 服务器组件渲染到客户端发生了什么&#xff1f; 请求到达服务器 用户在浏览器中请求一个页面。 Next.js 服务器接收到这个请求&#xff0c;并根据路由找到相应的页面组件。服务器组件的渲染 Next.js 识别出请求的页面包含…

数据结构与算法——N叉树(自学笔记)

本文参考 N 叉树 - LeetBook - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 遍历 前序遍历&#xff1a;A->B->C->E->F->D->G后序遍历&#xff1a;B->E->F->C->G->D->A层序遍历&#xff1a;A->B->C->D->…

SpringSecurity6

1.快速入门 2.SpringSecurity底层原理 使用的是委托过滤器,委托过滤器实际上就是 sevlet 过滤器 将自己放入Sevlet环境下 然后里面是一个 过滤器链代理 代理类下又是一个代理过滤器链的集合, 对于不同请求可以有不同的过滤器链, springsecurity有个默认的过滤器链 Defau…

芯片测试-RF中的S参数,return loss, VSWR,反射系数,插入损耗,隔离度等

RF中的S参数&#xff0c;return loss, VSWR&#xff0c;反射系数&#xff0c;插入损耗&#xff0c;隔离度 &#x1f4a2;S参数&#x1f4a2;&#x1f4a2;S11与return loss&#xff0c;VSWR&#xff0c;反射系数&#x1f4a2;&#x1f4a2;S21&#xff0c;插入损耗和增益&#…

前端页面或弹窗在线预览文件的N种方式

需求&#xff1a;后端返回给前端一个地址后&#xff0c;在前端页面上或则在弹框中显示在线的文档、表格、图片、pdf、video等等&#xff0c;嵌入到前端页面 方式一&#xff1a; 使用vue-office 地址&#xff1a;vue-office简介 | vue-office 个人感觉这个插件是最好用的&#x…

剪映自动批量替换视频、图片素材教程,视频批量复刻、混剪裂变等功能介绍

一、三种批量替换模式的区别 二、混剪裂变替换素材 三、分区混剪裂变替换素材 四、按组精确替换素材 五、绿色按钮教程 &#xff08;一&#xff09;如何附加音频和srt字幕 &#xff08;二&#xff09;如何替换固定文本的内容和样式 &#xff08;三&#xff09;如何附加…

【天地图】HTML页面实现车辆轨迹、起始点标记和轨迹打点的完整功能

目录 一、功能演示 二、完整代码 三、参考文档 一、功能演示 运行以后完整的效果如下&#xff1a; 点击开始&#xff0c;小车会沿着轨迹进行移动&#xff0c;点击轨迹点会显示经纬度和时间&#xff1a; 二、完整代码 废话不多说&#xff0c;直接给完整代码&#xff0c;替换…

Node报错:npm error code ETIMEDOUT

1、报错详细信息 npm error code ETIMEDOUT npm error syscall connect npm error errno ETIMEDOUT npm error network request to https://registry.npmjs.org/express failed, reason: connect ETIMEDOUT 104.16.1.35:443 npm error network This is a problem related to ne…

FPGA工具链及功能介绍

一、处理流程 把verilog等源码&#xff0c;变为FPGA中可执行的比特流文件&#xff0c;主要包含这些步骤&#xff1a; 步骤功能转译将verilog代码转化为更详细的语法&#xff0c;增加更多细节内容技术映射将每个vrilog用到的模块&#xff0c;对应到FPGA的物理器件上优化优化冗余…