数据结构-如何实现一个队列?逐步解析与代码示例(超详细)

在这里插入图片描述

文章目录

  • 前言
  • 1.队列的基本概念
  • 2.链表与数组实现队列的区别
    • 2.1数据存储结构
    • 2.2性能
    • 2.3内存使用
  • 3.为什么选择链表实现队列?
  • 4.结构定义
    • 函数声明
  • 5.核心操作
    • 5.1初始化 (`QInit`)
    • 5.2销毁 (`QDestroy`)
    • 5.3入队 (`QPush`)
    • 5.4出队 (`QPop`)
  • 6.队列的查询操作
    • 6.1队首元素 (`QueueFront`)
    • 6.2队尾元素 (`QueueBack`)
  • 7.辅助函数
    • 7.1判断空 (`QueueEmpty`)
    • 7.2队列大小 (`QueueSize`)
  • 总结

前言

在计算机科学中,队列是一种非常基础且广泛使用的数据结构。它的工作原理类似于现实生活中的排队:先来的先服务(FIFO, First-In-First-Out)。在本文中,我们将深入探讨如何在C语言中使用链表实现一个队列,并解析相关的代码实现。


1.队列的基本概念

队列是一种遵循先进先出原则的线性数据结构。在队列中,添加操作(入队)发生在一端(队尾),而移除操作(出队)则发生在另一端(队首)。这种结构在多种场景中非常有用,如任务调度、数据缓冲等。
在这里插入图片描述


2.链表与数组实现队列的区别

当使用数组和链表实现队列时,主要区别在于数据存储结构、性能和内存使用:

2.1数据存储结构

数组:队列使用一个固定大小的数组。当数组满时,需要进行扩容,这可能涉及复制整个数组到新的内存位置。
链表:队列使用动态分配的节点,每个节点包含数据和指向下一个节点的指针。不需要事先分配固定大小的空间。

2.2性能

数组:入队和出队操作通常是O(1)复杂度,但当数组需要扩容时,复杂度会增加。
链表:由于不需要扩容,入队和出队操作始终保持O(1)复杂度。

2.3内存使用

数组:可能会有未使用的预留空间,特别是在队列大小波动较大时。
链表:每个元素单独分配,无需预留额外空间,但每个节点需要额外存储指针,略增加内存使用。


3.为什么选择链表实现队列?

在C语言中,链表是一种常用的数据结构,用于创建动态大小的序列。相比于数组实现,链表实现的队列具有动态分配内存的优点,不受固定大小的限制。

与基于数组的队列实现相比,链表实现的队列具有以下优势:

动态大小:不受固定长度的限制,可以根据需要动态扩展或收缩。
内存利用:每个元素仅在需要时分配内存,减少了空间浪费。
性能优化:避免了数组实现中可能发生的元素搬移操作。

接下来我们来用链表进行实现队列。


4.结构定义

该队列的实现基于两种结构:QueueNode 和 Queue。

typedef int QDataType; // 定义队列数据类型为int// 队列节点的结构体定义
typedef struct QueueNode
{QDataType val; // 节点存储的数据struct QueueNode* next; // 指向下一个节点的指针
} QNode;// 队列的结构体定义
typedef struct Queue
{QNode* phead; // 指向队列头部的指针QNode* ptail; // 指向队列尾部的指针int size; // 队列的大小
} Queue;

这里,QueueNode 表示队列中的每个节点,包含一个数据字段和一个指向下一个节点的指针。而Queue结构则持有指向队列头部和尾部的指针,并跟踪队列的当前大小。

函数声明

// 函数声明
void QInit(Queue* pq); // 初始化队列
void QDestroy(Queue* pq); // 销毁队列void QPush(Queue* pq, QDataType x); // 向队列中添加元素
void QPop(Queue* pq); // 从队列中移除元素QDataType QueueFront(Queue* pq); // 获取队列头部元素
QDataType QueueBack(Queue* pq); // 获取队列尾部元素bool QueueEmpty(Queue* pq); // 检查队列是否为空
int QueueSize(Queue* pq); // 获取队列的大小

5.核心操作

5.1初始化 (QInit)

初始化函数设置队列的头部和尾部指针为NULL,大小为0。这是创建队列的必要步骤。
在这里插入图片描述

// 初始化队列
void QInit(Queue* pq)
{assert(pq); // 断言队列指针非空pq->phead = pq->ptail = NULL; // 将头指针和尾指针都设为NULLpq->size = 0; // 将队列大小设置为0
}

5.2销毁 (QDestroy)

销毁函数释放队列中所有节点的内存,并重置队列的状态。这是队列不再使用时的清理步骤。
在这里插入图片描述

// 销毁队列
void QDestroy(Queue* pq)
{assert(pq); // 断言队列指针非空QNode* cur = pq->phead;while (cur){QNode* next = cur->next; // 保存下一个节点free(cur); // 释放当前节点cur = next; // 移动到下一个节点}pq->phead = NULL; // 将头指针设为NULLpq->ptail = NULL; // 将尾指针设为NULLpq->size = 0; // 将队列大小设置为0
}

5.3入队 (QPush)

在队列的尾部添加一个新元素。如果队列为空,则新元素同时成为头部和尾部元素。
在这里插入图片描述

// 向队列中添加元素
void QPush(Queue* pq, QDataType x)
{assert(pq); // 断言队列指针非空QNode* newNode = (QNode*)malloc(sizeof(QNode)); // 分配新节点内存if (newNode == NULL){perror("malloc fail"); // 内存分配失败处理exit(-1);}newNode->val = x; // 设置新节点的值newNode->next = NULL; // 新节点的下一个节点为NULLif (pq->ptail == NULL) // 如果队列为空{pq->phead = pq->ptail = newNode; // 队列头尾都指向新节点}else{pq->ptail->next = newNode; // 将新节点接到队列尾部pq->ptail = newNode; // 更新尾指针}pq->size++; // 队列大小增加
}

5.4出队 (QPop)

从队列的头部移除一个元素。如果队列为空,调整尾部指针。
在这里插入图片描述

// 从队列中移除元素
void QPop(Queue* pq)
{assert(pq); // 断言队列指针非空assert(pq->phead); // 断言队列不为空QNode* Del = pq->phead; // 保存要删除的节点pq->phead = pq->phead->next; // 更新头指针free(Del); // 释放节点内存if (pq->phead == NULL) // 如果队列变空{pq->ptail = NULL; // 更新尾指针}pq->size--; // 队列大小减少
}

6.队列的查询操作

6.1队首元素 (QueueFront)

返回队列头部节点的值,但不移除它。
在这里插入图片描述

// 获取队列头部元素的值
QDataType QueueFront(Queue* pq)
{assert(pq); // 断言队列指针非空assert(pq->phead); // 断言队列不为空return pq->phead->val; // 返回头部元素的值
}

6.2队尾元素 (QueueBack)

返回队列尾部节点的值。
在这里插入图片描述

// 获取队列尾部元素的值
QDataType QueueBack(Queue* pq)
{assert(pq); // 断言队列指针非空assert(pq->ptail); // 断言队列不为空return pq->ptail->val; // 返回尾部元素的值
}

7.辅助函数

7.1判断空 (QueueEmpty)

检查队列是否为空。
在这里插入图片描述

// 检查队列是否为空
bool QueueEmpty(Queue* pq)
{assert(pq); // 断言队列指针非空return pq->phead == NULL; // 如果头指针为NULL,则队列为空
}

7.2队列大小 (QueueSize)

返回队列中节点的数量。
在这里插入图片描述

// 获取队列的大小
int queueSize(Queue* pq)
{return pq->size; // 返回队列的大小
}

总结

理解队列的不同实现方式对于选择最适合特定应用场景的数据结构至关重要。链表实现的队列提供了灵活性和一致的性能,特别适用于大小不确定或需要频繁扩展的场景。而数组实现则在空间预分配和随机访问方面更有优势。深入了解这些差异,可以帮助程序员做出更明智的选择。我希望这篇文章能够帮助你更好地理解和实现队列。如果你有任何问题或想分享你的经验,请在评论区留言。

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

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

相关文章

如何将语音版大模型AI接入自己的项目里(语音ChatGPT)

如何将语音版大模型AI接入自己的项目里语音ChatGPT 一、语音版大模型AI二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例5、智能生成API代码 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、语音版大模型AI 基于阿里通义千问、百…

分享5款实用的小工具,提升你的工作效率

​ 工作中,简单而实用的小工具能够为我们带来事半功倍的效果。这五款工具可能是你工作效率提升的关键。 1.云存储——Dropbox ​ Dropbox是一款流行的云存储服务,可以让你在不同的设备上同步和访问你的文件。你可以将你的文件上传到Dropbox的服务器上&…

绝缘电阻测试仪的测量范围有多少?它的测量方法是什么?

绝缘电阻测试仪广泛应用于设备检测和故障排除。它广泛应用于电力检测行业。甚至可以说,电力设备离不开绝缘电阻测试仪设备。对于许多经验丰富的电力测试工人来说,绝缘电阻测试仪的常规测量范围和方法应该非常清楚。在本文中,我们将向一些新的…

学之思开源考试系统是一款 java + vue 的前后端分离的考试系统

学生系统功能 模块介绍登录用户名、密码注册年级、用户名、密码任务中心管理员发布的年级任务,每个学生只能做一次考试题干支持文本、图片、数学公式、表格等,学生答题支持:文本固定试卷可重复练习、自行批改的试卷时段试卷在时间限制内&…

关于chatglm3 function calling的理解

ChatGLM3-6B开源了工具调用,好奇他是怎么实现的,所以写了这个文章记录。 一、测试官方的示例 官方给的示例很简单,只不过给的两个函数 track 和 text-to-speech 没有具体的实现,模型的输出也只是给出了需要调用的函数名和参数。剩…

【C语言】指针详解(四)

目录 1.assert断言 2.指针的使用和传址调用 2.1strlen的模拟使用 2.2传值调用和传址调用 1.assert断言 assert.h头文件定义了宏 assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”。 例如…

多模态——CLIP:Contrastive Language-Image Pre-training解读

前言 随着人工智能技术的不断进步,多模态成为备受瞩目的研究方向。多模态技术旨在融合不同类型的数据和信息,以实现更准确、高效的人工智能应用。有学者认为它代表了所有模型发展的最终趋势。这类模型旨在接受多种不同的输入方式,例如图像、…

OpenStack搭建和部署

Centos官网qcow2镜像修改root账号密码,开启ssh等 wget http://172.16.20.10/vmtemplate/KVM/wangrui/Debian/debian-10.2.0-openstack-amd64.qcow2 一、查看镜像文件信息 [debian-10.2-cloud] nameDebian 10.2.0 (Buster) Cloud osinfodebian10 archx86_64 fi…

Linux Debian12使用podman安装upload-labs靶场环境

一、upload-labs简介 PHP语言编写,持续收集渗透测试和CTF中针对文件上传漏洞的靶场,总共21关,每一关都包含着不同的上传绕过方式。 二、安装podman环境 Linux Debian系统如果没有安装podman容器环境,可以参考这篇文章先安装pod…

透过许战海矩阵洞察安记食品增长战略

引言:安记食品如果想实施增长战略,建议深耕招牌产品,走向全国市场,目前招牌产品咖哩和复合调味粉市场空间没有被全面释放出来,需要科学的产品战略作为支撑。安记食品选择功能性产品方向是正确的,但“功能性”需要一个大品类作为载体,牛奶,饮料是最大的载…

网站被恶意扫描怎么办(上WAF)

在网络安全领域,有一大类工具被广泛使用,且作用不可忽视,它就是网络安全扫描器。扫描器是一种专门设计用来评估计算机、网络或者应用中已知的弱点的计算机程序,但是很多人恶意使用,找到网站弱点进行攻击。 扫描器的种…

K8S理论

kubernetes:8个字母省略,就是k8s 自动部署自动扩展和管理容器化部署的应用程序的一个开源系统 k8s是负责自动化运维管理多个容器化程序的集群,是一个功能强大的容器编排工具 分布式和集群化的方式进行容器化管理 版本有1.15 .1.18 .1.20 …

问答区故意在结题前回答混赏金的狗

此贴专记录CSDN问答社区里面,一些回答者在临近结题时胡乱回答,只为分取结题赏金的人。 所有图片均为事实,绝无半点虚假。各位看官可以自行搜索问题题目或者通过查看此人回答求证 所有图片均为事实,绝无半点虚假。各位看官可以自行…

c语言中数据结构

一、结构体的由来 1. 数据类型的不足 C语言中,基本数据类型只有整型、字符型、浮点型等少数几种,无法满足复杂数据类型的需要。 2. 数组的限制 虽然数组可以存储多个同类型的数据,但是数组中的元素个数是固定的,无法动态地改变…

渗透测试——1.3计算机网络基础

一、黑客术语 1、肉鸡:被黑客攻击电脑,可以受黑客控制不被发现 2、端口(port):数据传输的通道 3、弱口令:强度不高,容易被猜到的口令、密码 4、客户端:请求申请电脑(…

10. UVM Environment

环境为agents, scoreboards和其他验证组件(包括有助于在 SoC 级别重用块级环境组件的其他环境类)提供良好的层次结构和容器。用户定义的 env 类必须从 uvm_env 类扩展。 10.1 uvm_env class hierarchy 类声明: virtual class uvm_env extend…

k8s的二进制部署(一)

k8s的二进制部署:源码包部署 环境: k8smaster01: 20.0.0.71 kube-apiserver kube-controller-manager kube-schedule ETCD k8smaster02: 20.0.0.72 kube-apiserver kube-controller-manager kube-schedule Node节点01: 20.0.0.73 kubelet kube-pr…

nodejs+vue+ElementUi大学新生入学系统的设计与实现1hme0

采用B/S模式架构系统,开发简单,只需要连接网络即可登录本系统,不需要安装任何客户端。开发工具采用VSCode,前端采用VueElementUI,后端采用Node.js,数据库采用MySQL。 涉及的技术栈 1) 前台页面…

Stable Diffusion模型原理

1 Stable Diffusion概述 1.1 图像生成的发展 在Stable Diffusion诞生之前,计算机视觉和机器学习方面最重要的突破是 GAN(Generative Adversarial Networks 生成对抗网络)。GAN让超越训练数据已有内容成为可能,从而打开了一个全新…

Elasticsearch8.x结合OpenAI CLIP模型实现图搜图及文搜图功能

前言 在当今大数据时代,搜索引擎已经是许多应用的核心组件之一,近年随着大模型以及AI技术(如:自然语言处理NLP)的流行,这些技术的结合将会创造出更多的应用场景,比如:电商商品搜索、…