【算法与数据结构】 C语言实现单链表队列详解

请添加图片描述

文章目录

  • 📝队列
  • 🌠 数据结构设计
    • 🌉初始化队列函数
  • 🌠销毁队列函数
    • 🌉入队函数
  • 🌠出队函数
    • 🌉获取队首元素函数
  • 🌠获取队尾元素函数
    • 🌉 判断队列是否为空函数
    • 🌉获取队列大小函数
  • 🌠测试
    • 🌉 总源代码
  • 🚩总结


📝队列

前面我们学习了队列的顺序表的实现,本节将用单链表实现队列。
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。下面我们先复习一下队列的基本概念:
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
在这里插入图片描述
在这里插入图片描述

🌠 数据结构设计

首先,我们需要定义队列节点的数据结构和队列的数据结构:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>// 定义队列节点数据结构
typedef int QDataType;//定义了一个新的数据类型 QDataTypetypedef struct QueueNode 
{QDataType* data;//数据指针struct QueueNode* next;//指向下一个节点的指针。
} QNode;

当我们去定义队列的出队和入队时需要用到二级指针,这样传递指针的指针操作起来会有些繁琐,也不能更符合队列的逻辑结构,二级指针代码如下:

//入队列
void QueuePush(QNode** pphead, QNode** pptail);
//出队列
void QueuePop(QNode** pphead, QNode** pptail);

虽然使用指向指针的指针也可以实现队列的功能,但是使用结构体封装的方式更符合常见的数据结构和面向对象编程的思想,能够提高代码的可读性、可维护性和易用性。

// 定义队列数据结构
typedef struct Queue 
{QNode* phead;//指向队列的头节点(队首)QNode* ptail;//指向队列的尾节点(队尾)。int size;//表示队列中元素的数量。
} Queue;

对于队列这种数据结构,使用指向队列头部和尾部的指针以及队列大小的方式进行封装有以下好处:

  1. 封装性更好

    • 使用 Queue 结构体将队列的头部指针、尾部指针和大小封装在一起,更符合面向对象编程的思想,提高了代码的封装性和可维护性。
    • 将队列的相关信息放在一个结构体中,使得对队列的操作更加直观和清晰,降低了出错的可能性。
  2. 操作更加直观

    • 在进行队列操作时,使用 Queue 结构体可以直接通过 pheadptail 指针来操作队列的头部和尾部,而无需传递指向指针的指针。这样使得代码更加清晰,不易出错。
  3. 提高代码的可读性和可维护性

    • 使用 Queue 结构体可以将队列的相关信息集中在一起,使得代码更加清晰易读。
    • 在函数参数中传递 Queue* 类型的指针,比传递指向指针的指针更加直观和易懂,减少了理解代码的难度。

由于队列是先进先出,并且单向的,而头节点是哨兵位,要也可以不要也可以,本文没有用多出使用一个头节点。
在这里插入图片描述

🌉初始化队列函数

先确保传入的队列指针 pq 不为空,将队列的头指针、尾指针置为空,大小置为0。

void QueueInit(Queue* pq)
{assert(pq); // 断言队列指针是否为空pq->phead = NULL; // 初始化头节点指针为空pq->ptail = NULL; // 初始化尾节点指针为空  pq->size = 0; // 初始化队列大小为0
}

🌠销毁队列函数

首先断言队列指针是否为空,cur从头节点开始遍历队列,保存cur下一个节点的指针,释放cur节点内存,cur移到下一个节点,遍历完整个队列后,将头尾节点指针和大小都重置为初始状态

void QueueDestroy(Queue* pq) 
{assert(pq); // 断言队列指针是否为空QNode* cur = pq->phead; // cur指向队列头节点while (cur) {  QNode* next = pq->phead->next; // 保存cur下一个节点的指针free(cur); // 释放cur节点内存cur = next; // cur指针移到下一个节点}pq->phead = pq->ptail = NULL; // 重置头尾节点指针为空pq->size = 0; // 重置队列大小为0}

🌉入队函数

将新的数据 x 入队,要分配一个新的节点 newnode,将数据 x 存储在节点中。根据队列是否为空,将新节点连接到队列的尾部,并更新尾指针。如果队列为空,则同时更新头指针。
增加队列的大小。

void QueuePush(Queue* pq, QDataType* x) 
{assert(pq); // 断言队列指针是否为空QNode* newnode = (QNode*)malloc(sizeof(QNode)); // 申请新节点内存if (newnode == NULL) { // 申请失败perror("malloc fail"); return;}newnode->data = x; // 设置新节点数据newnode->next = NULL; // 设置新节点next指针为空if (pq->ptail){ // 如果队列不为空pq->ptail->next = newnode; // 尾节点指向新节点pq->ptail = newnode; // 更新尾节点指针} else { // 队列为空pq->phead = pq->ptail = newnode; // 头尾节点同时指向新节点}pq->size++; // 队列大小增加1}

在这里插入图片描述

🌠出队函数

出队,即删除队列中的队首元素。首先检查队列是否为空,如果为空则直接返回。如果队列只有一个节点,则直接释放这个节点,同时将头尾指针置为空。如果队列有多个节点,则释放队首节点,并将头指针指向下一个节点。减小队列的大小。

void QueuePop(Queue* pq) 
{assert(pq); // 断言队列指针是否为空if (pq->phead == NULL) return; // 队列为空直接返回assert(pq->phead != NULL); // 断言头节点不为空if (pq->phead->next == NULL){ // 队列只有一个节点free(pq->phead); // 释放头节点pq->phead = pq->ptail = NULL; // 头尾节点同时置空} else { // 队列有多个节点QNode* next = pq->phead->next; // 保存头节点的下一个节点free(pq->phead); // 释放头节点pq->phead = next; // 头节点指向下一个节点}pq->size--; // 队列大小减1}

在这里插入图片描述

🌉获取队首元素函数

获取队列的队首元素,得首先确保队列不为空,然后返回队首节点中存储的数据。

QDataType QueueFront(Queue* pq){assert(pq);assert(pq->phead != NULL);return pq->phead->data;
}

🌠获取队尾元素函数

获取队列的队尾元素,得首先确保队列不为空,然后返回队尾节点中存储的数据。

QDataType QueueBack(Queue* pq){assert(pq);assert(pq->ptail != NULL);return pq->ptail->data;
}

🌉 判断队列是否为空函数

判断队列是否为空,通过检查队列的大小是否为0来确定队列是否为空。

bool QueueEmpty(Queue* pq) 
{assert(pq);return pq->size == 0;
}

🌉获取队列大小函数

获取队列的大小,即队列中元素的数量。

int QueueSize(Queue* pq) 
{assert(pq);return pq->size;
}

🌠测试

# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "Queue.h" int main() 
{Queue queue;QueueInit(&queue);printf("Queue is empty: %s\n", QueueEmpty(&queue) ? "true" : "false");printf("Pushing elements into the queue...\n");for (int i = 1; i <= 5; ++i) {printf("Pushing %d\n", i);QueuePush(&queue, i);}printf("Queue size: %d\n", QueueSize(&queue));printf("Queue front: %d\n", QueueFront(&queue));printf("Queue back: %d\n", QueueBack(&queue));printf("Popping elements from the queue...\n");while (!QueueEmpty(&queue)) {printf("Front: %d\n", QueueFront(&queue));QueuePop(&queue);}printf("Queue is empty: %s\n", QueueEmpty(&queue) ? "true" : "false");QueueDestroy(&queue);return 0;
}

代码效果图:
在这里插入图片描述

🌉 总源代码

Queue.c

#include "Queue.h"void QueueInit(Queue* pq)
{assert(pq); // 断言队列指针是否为空pq->phead = NULL; // 初始化头节点指针为空pq->ptail = NULL; // 初始化尾节点指针为空  pq->size = 0; // 初始化队列大小为0
}void QueueDestroy(Queue* pq)
{assert(pq); // 断言队列指针是否为空QNode* cur = pq->phead; // cur指向队列头节点while (cur){QNode* next = pq->phead->next; // 保存cur下一个节点的指针free(cur); // 释放cur节点内存cur = next; // cur指针移到下一个节点}pq->phead = pq->ptail = NULL; // 重置头尾节点指针为空pq->size = 0; // 重置队列大小为0}void QueuePush(Queue* pq, QDataType* x)
{assert(pq); // 断言队列指针是否为空QNode* newnode = (QNode*)malloc(sizeof(QNode)); // 申请新节点内存if (newnode == NULL){ // 申请失败perror("malloc fail");return;}newnode->data = x; // 设置新节点数据newnode->next = NULL; // 设置新节点next指针为空if (pq->ptail){ // 如果队列不为空pq->ptail->next = newnode; // 尾节点指向新节点pq->ptail = newnode; // 更新尾节点指针}else{ // 队列为空pq->phead = pq->ptail = newnode; // 头尾节点同时指向新节点}pq->size++; // 队列大小增加1}//出队列
void QueuePop(Queue* pq)
{assert(pq); // 断言队列指针是否为空if (pq->phead == NULL)return; // 队列为空直接返回assert(pq->phead != NULL); // 断言头节点不为空if (pq->phead->next == NULL){ // 队列只有一个节点free(pq->phead); // 释放头节点pq->phead = pq->ptail = NULL; // 头尾节点同时置空}else{ // 队列有多个节点QNode* next = pq->phead->next; // 保存头节点的下一个节点free(pq->phead); // 释放头节点pq->phead = next; // 头节点指向下一个节点}pq->size--; // 队列大小减1}QDataType QueueFront(Queue* pq)
{assert(pq);assert(pq->phead != NULL);return pq->phead->data;
}QDataType QueueBack(Queue* pq)
{assert(pq);//暴力检查assert(pq->ptail != NULL);return pq->ptail->data;
}bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

🚩总结

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何让uni-app开发的H5页面顶部原生标题和小程序的顶部标题不一致?

如何让标题1和标题2不一样&#xff1f; 修改根目录下的App.vue&#xff08;核心代码如下&#xff09; <script>export default {onLaunch() {// 监听各种跳转----------------------------------------[navigateTo, redirectTo, reLaunch, switchTab, navigateBack, ].…

Leetcode 226. 翻转二叉树

心路历程&#xff1a; 翻转一瞬间没什么思路&#xff0c;其实就是挨个把每个结点的左右子树都翻转了。主要不要按照左右子树去思考&#xff0c;要按照结点去思考。 翻转既可以从上到下翻转&#xff08;前序遍历&#xff09;&#xff0c;也可以从下到上翻转&#xff08;后序遍历…

等保测评-Windows服务器

安全计算环境 身份鉴别 a)应对登录的用户进行身份标识和鉴别&#xff0c;身份标识具有唯一性&#xff0c;身份鉴别信息具有复杂度要求并定期更换 winr //运行 secpol.msc //本地安全策略&#xff0c;点击账户策略中的密码策略 注意修改的时候确保当前密码是满足条件的 b)应…

应急响应-Linux(1)

应急响应-Linux(1) 黑客的IP地址 思路&#xff1a; 一般系统中马之后会有进程连接黑客的主机&#xff0c;可以使用netstat -anpt查看下当前进程的连接&#xff0c;此处查看到没有后 &#xff0c;可以从系统服务开始查找&#xff0c;系统的服务日志一般都会保存相关访问信息&…

idea 没有代码提示解决方法

itellij idea 没有代码提示解决方法 今天写代码发现没有代码提示了&#xff0c;很难受。 直接上解决方法 设置 File-Settings-Editor-General-Code Completion&#xff1a;勾选Show suggestrions as you type 我的是这个问题&#xff0c;勾选上就ok了 取消节能模式 如果…

智慧公园:AI智能分析网关V4城市公园视频智能监管方案

一、背景分析 随着天气渐渐转暖&#xff0c;城市公园的花卉也逐渐盛开&#xff0c;春暖花开时节&#xff0c;前往公园赏花游玩的城市居民也渐渐多起来&#xff0c;因此安全问题也成为相关监管部门的重要管理任务之一。随着科技的不断进步&#xff0c;智能监控技术已经成为现代…

WPS制作甘特图

“ 甘特图&#xff08;Gantt chart&#xff09;又称为横道图、条状图&#xff08;Bar chart&#xff09;&#xff0c;通过条状图来显示项目、进度和其他时间相关的系统进展的内在关系随着时间进展的情况。” 设置基础样式 设置行高 设置宽度 准备基础数据 计算持续时间 …

.NET Core 服务实现监控可观测性最佳实践

前言 本次实践主要是介绍 .Net Core 服务通过无侵入的方式接入观测云进行全面的可观测。 环境信息 系统环境&#xff1a;Kubernetes编程语言&#xff1a;.NET Core ≥ 2.1日志框架&#xff1a;Serilog探针类型&#xff1a;ddtrace 接入方案 准备工作 DataKit 部署 DataK…

【MySQL】MVCC多版本并发控制

MVCC&#xff08;Multi-Version Concurrency Control&#xff09; 多版本并发控制&#xff0c;用于解决数据库并发访问中&#xff0c;数据一致性问题。它通过在读写操作期间保存多个数据版本&#xff0c;以提供并发事务间的隔离性&#xff0c;从而避免了传统的锁机制所带来的资…

2024-03-23 问AI: 介绍一下深度学习中的ReLU函数

文心一言 ReLU&#xff08;Rectified Linear Unit&#xff09;函数是深度学习领域中常用的一种激活函数。它具有简单、计算高效且在某些情况下能有效缓解梯度消失问题等优点&#xff0c;因此在神经网络中得到了广泛的应用。 ReLU函数的定义非常简单&#xff0c;其数学表达式为…

MapReduce学习问题记录

1、如何跳过对某行数据的处理 第一行数据是字段名不需要处理&#xff0c;我们知道第一行偏移量是0&#xff08;行记录的时候是从数组首地址开始&#xff0c;到了行标识符进行一次计数&#xff0c;这个计数就是行偏移量&#xff0c;从0开始&#xff09;&#xff0c;我们根据偏移…

1+x中级题目练习复盘(八)

SQL 语句中进行 group by 分组时&#xff0c;可以不写 where 子句 在使用 select 语句进行查询分组时&#xff0c;如果希望去掉不满足条件的分组&#xff0c;使用 having 子句File 类的 isDirectory() 方法可以判断文件是否为目录 在使用 select 语句进行查询分组时&#xff0…

基于Matlab的眼底图像血管分割,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

qt5-入门-国际化

参考&#xff1a; Qt 国际化(上)_w3cschool https://www.w3cschool.cn/learnroadqt/fwkx1j4j.html QT5实现语言国际化&#xff08;中英文界面动态切换&#xff0c;超详细&#xff09;_qt qevent::languagechange-CSDN博客 https://blog.csdn.net/m0_49047167/article/details/…

奇舞周刊第523期:来自 rust 生态的强烈冲击?谈谈 Leptos 在语法设计上的精妙之处...

奇舞推荐 ■ ■ ■ 来自 rust 生态的强烈冲击&#xff1f;谈谈 Leptos 在语法设计上的精妙之处 过去很长一段时间&#xff0c;前端框架们都在往响应式的方向发展。同时又由于 React hooks 的深远影响&#xff0c;函数式 响应式成为了不少前端心中最理想的前端框架模样。Solid …

从JVM的退出机制分析Java程序的优雅关闭退出

前言 Java程序启动从main函数开始启动&#xff0c;是程序入口和主线程&#xff0c;但程序会在什么时候结束&#xff1f;为什么有的Java程序在启动后很快就结束了&#xff0c;比如HelloWorld程序&#xff0c;有的程序却能一直在运行&#xff0c;比如Tomcat启动后就一直保持进程…

Excel数字乱码怎么回事 Excel数字乱码怎么调回来

在日常工作中&#xff0c;Excel是我们最常使用的数据处理软件之一&#xff0c;它强大的功能使得数据处理变得既简单又高效。然而&#xff0c;用户在使用Excel时偶尔会遇到数字显示为乱码的问题&#xff0c;这不仅影响了数据的阅读&#xff0c;也大大降低了工作效率。那么&#…

RIPGeo代码理解(六)main.py(运行模型进行训练和测试)

​代码链接:RIPGeo代码实现 ├── preprocess.py # 预处理数据集并为模型运行执行IP聚类 ├── main.py # 运行模型进行训练和测试 ├── test.py #加载检查点,然后测试 一、导入各种模块和数据库 import torch.nnfrom lib.utils import * import argparse i…

数学算法(算法竞赛、蓝桥杯)--最大公约数,欧几里得算法

1、B站视频链接&#xff1a;G05 最大公约数 欧几里得算法_哔哩哔哩_bilibili 题目链接&#xff1a;[NOIP2001 普及组] 最大公约数和最小公倍数问题 - 洛谷 #include <bits/stdc.h> using namespace std; typedef long long LL; LL x,y,ans;LL gcd(LL a,LL b){return b0?…

MongoDB知识

1、部署MongoDB &#xff08;1&#xff09;new好一个mongo文件之后执行 &#xff08;出现mongodb.key&#xff09;记得放行端口 openssl rand -base64 666 > mongodb.key &#xff08;2&#xff09;放到一个docker-compose.yml之后docker-compose up -d执行 version: 3.…