当前位置: 首页 > news >正文

C语言数据结构初阶(7)----队列

· CSDN的uu们,大家好。这里是C语言数据结构的第七讲。
· 目标:前路坎坷,披荆斩棘,扶摇直上。
· 博客主页:@姬如祎
  1. 队列的基础知识

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。

2. 链式存储OR顺序存储

线性表有顺序存储和链式存储,栈是线性表,具有这两种存储方式。同样,队列作为一种特殊的线性表,也同样存在这两种存储方式。

2.1 队列顺序存储的不足

顺序存储的队列就是用数组哈!数组下标为0的一端即是队头。入队列就是在队尾插入一个数据,对于数组而言,不需要移动任何元素,因此时间复杂度是O(1)。

但是在出队列时如果徐翔浪费任何空间的话,就需要将全部数据前移。时间复杂度也就是O(N)。

当然你也可以参照数组模拟队列的思路,维护两个指针:front,rear。front指向队头的元素,rear指向队尾元素。出队列的时候仅需要将front加一即可。这样以来就有空间的浪费。如此看来使用顺序结构来实现队列不是很理想。如果已经确定了队列的元素个数,你可以使用循环队列,这样一来数组来实现队列就是非常完美的事了!关于循环队列,会在栈与队列刷题的那一节提及。

2.2 队列的链式存储

队列的链式存储结构,其实就是线性表的单链表,只不过他只能尾进头出而已,我们把它简称为链队列。

入队就是链表的尾插,我们知道如果不做任何处理的话,单链表尾插的时间复杂度是O(N),为了使得时间复杂度是O(1),我们需要维护一个指针,让他指向链表的尾节点,即是队尾。

出队就是链表的头删,这个操作的时间复杂度是O(1)。

队列的链式存储能够在O(1) 的时间复杂度内实现各种操作,也没有浪费空间。所以我们优先考虑用链表实现队列。

因为我们既要维护指向链表头结点的指针,又要维护指向链表尾节点的指针,并且我们还需要维护一个变量size来记录队列的大小,所以我们将这些变量封装在一个结构体里面,便于操作。

当然你也可以不这么做,那么在函数传参的时候你就需要传多个参数。并且涉及指针的改变,形参还必须是二级指针。

如果有不理解的地方请参考:
http://t.csdn.cn/QXmxD

是不是相当滴麻烦。那么我们就将它们封装成一个结构体吧!

3. 函数接口一览

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>//方便维护
typedef int QueueDataType;//实现队列的链表的节点
typedef struct QueueNode
{struct QueueNode* next;QueueDataType data;
} QueueNode;//将需要维护的head,tail,size封装成结构体
typedef struct Queue
{QueueNode* head;QueueNode* tail;int size;
} Queue;//队列的初始化
void QueueInit(Queue* pq);//队列的销毁
void QueueDestory(Queue* pq);//队列是否为空
bool QueueEmpty(Queue* pq);//队尾增加元素
void QueuePush(Queue* pq, QueueDataType x);//出队列
void QueuePop(Queue* pq);//队列的元素个数
int QueueSize(Queue* pq);//队头元素
QueueDataType QueueFront(Queue* pq);//队尾元素
QueueDataType QueueBack(Queue* pq);

4. 函数接口的实现

4.1 void QueueInit(Queue* pq) 函数的实现

队列的初始化需要我们做什么呢?将我们封装的结构体里面的变量赋初值!注意对pq的断言,防止发生空指针的解引用。

//队列的初始化
void QueueInit(Queue* pq)
{//断言assert(pq);//赋初值pq->head = pq->tail = NULL;pq->size = 0;
}

4.2 void QueueDestory(Queue* pq) 函数的实现

销毁队列的主要目的:释放堆区开辟的空间,也就是释放单链表的每一个节点。这当然需要遍历呀!我们维护一个指针,例如:cur,用其遍历单链表,让后将遍历到的节点释放。注意保存释放节点的下一个节点就行。还别忘了将我们维护的三个变量:head,tail,size处理一下,防止出现野指针。

//队列的销毁
void QueueDestory(Queue* pq)
{//断言,防止空指针的解引用assert(pq);//cur指针遍历链表QueueNode* cur = pq->head;while (cur){//保存cur指针的下一个节点QueueNode* next = cur->next;//释放cur指向的节点free(cur);//迭代cur = next;}//处理pq->head = pq->tail = NULL;pq->size = 0;
}

4.3 bool QueueEmpty(Queue* pq) 函数的实现

这个函数的实现有两种方式:

1:就是判断一下头指针是否为空指针就行,头指针为空就说明了链表没有节点,也就说明了队列为空。队列为空时,尾节点此时也是空指针的哦!

2:我们不是维护了一个变量size嘛,其记录了队列的大小,我们只需要判断size是不是0就ok啦!

//队列是否为空
bool QueueEmpty(Queue* pq)
{//断言,防止空指针的解引用assert(pq);//one way//return pq->size == 0;//another wayreturn pq->head == NULL;
}

4.4 void QueuePush(Queue* pq, QueueDataType x) 函数的实现

前面也提到了,入队列的操作就是在链表的尾部插入节点。我们传入的是结构体的指针,想要改变结构体里面的head指针,tail指针是可以做到的,因此队列入数据时注意改变head指针和tail指针就行。

当队列为空时需要特殊处理一下:同时改变head和tail指向开辟的新节点。

//队尾增加元素
void QueuePush(Queue* pq, QueueDataType x)
{//断言,防止空指针的解引用assert(pq);//开辟新的节点QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));if (newNode == NULL){perror("QueuePush::malloc");exit(-1);}else{//数据域赋值newNode->data = x;newNode->next = NULL;//当队列为空时if (!pq->head){pq->head = pq->tail = newNode;}else{//链表不为空时pq->tail->next = newNode;pq->tail = newNode;}pq->size++;}
}

4.5 void QueuePop(Queue* pq)的实现

出队列操作,就是链表的头删。理论上我们只需要释放头结点,改变head指针的指向就行。但是,当队列剩下最后一个元素时如果还是仅改变head指针的指向,那么tail指针就会是一个野指针,这样做显然是不正确的。因此我们需要对队列只有一个元素的情况进行特殊判断。

//出队列
void QueuePop(Queue* pq)
{//防止空指针的解引用assert(pq);//队列为空不允许出队列assert(!QueueEmpty(pq));//one way//对一个节点的情况处理if (pq->head == pq->tail){free(pq->head);pq->head = pq->tail = NULL;}else{//队列中的元素大于一个QueueNode* next = pq->head->next;free(pq->head);pq->head = next;}//更新sizepq->size--;//another way// 都是处理队列中只有一个元素的情况,只不过实现的方式不同//QueueNode* next = pq->head->next;//free(pq->head);//pq->head = next;//pq->size--;//if (pq->head == NULL)//    pq->tail = NULL;
}

4.6 int QueueSize(Queue* pq)函数的实现

我们只需要返回我们维护的size变量的值就行。

//队列的元素个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

4.7 QueueDataType QueueFront(Queue* pq)函数的实现

我们维护了一个指针head,它指向的就是队头元素,返回该节点的打他即可。

//队头元素
QueueDataType QueueFront(Queue* pq)
{assert(pq);//队列为空不允许查看队头数据assert(!QueueEmpty(pq));return pq->head->data;
}

4.8 QueueDataType QueueBack(Queue* pq)函数的实现

我们维护了一个指针tail,它指向的就是队尾元素,我们只需要返回该节点的data即可。

//队尾元素
QueueDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;
}

相关文章:

C语言数据结构初阶(7)----队列

CSDN的uu们&#xff0c;大家好。这里是C语言数据结构的第七讲。 目标&#xff1a;前路坎坷&#xff0c;披荆斩棘&#xff0c;扶摇直上。 博客主页&#xff1a;姬如祎队列的基础知识队列&#xff08;queue&#xff09;是只允许在一端进行插入操作&#xff0c;而在另一端进行删除…...

代码随想录二刷 day01 | 704. 二分查找 27. 移除元素 977. 有序数组的平方

代码随想录二刷day01704. 二分查找27. 移除元素977. 有序数组的平方704. 二分查找 题目链接 做这种题最好现在纸上写一写&#xff0c;如果在大脑中想&#xff0c;可能一会就晕了。 二刷的时候发现了一个新的知识点 即&#xff1a; >>的作用 二分法第二种写法&#xff1a…...

Linux 终端、进程组、会话、守护进程

文章目录一、终端概念终端概念控制终端二、进程组概念进程组概述进程组相关 API会话会话概念会话相关 API创建会话注意事项守护进程守护进程介绍守护进程模型守护进程参考代码守护进程相关 API参考文章一、终端概念 终端概念 1、终端&#xff08;Terminal&#xff09; 终端是…...

你是否有潜质成为谷歌开发者专家?加入 GDE 成长计划,释放潜力!

谷歌开发者专家 (Google Developer Experts&#xff0c;GDE)&#xff0c;又称谷歌开发者专家项目&#xff0c;是由一群经验丰富的技术专家、具有社交影响力的开发者和思想领袖组成的全球性社区。通过在各项活动演讲以及各个平台上发布优质内容来积极助力开发者、企业和技术社区…...

安全防御之防火墙篇(二)

目录 1.防火墙如何处理双通道协议&#xff1f; 2.防火墙如何处理NAT&#xff1f; 3.防火墙支持哪些NAT技术&#xff0c;主要应用的场景是什么&#xff1f; 4.当内网PC通过公网域名解析访问内网服务器的时候&#xff0c;会存在什么问题&#xff0c;如何解决&#xff1f;请详细…...

设计必备,5个png免抠素材网站,建议收藏

做设计、PPT都需要用到大量的免抠素材&#xff0c;职场中熟练使用Photoshop的人毕竟是少数&#xff0c;也很少有人愿意花费时间去精细抠图。那这5个免抠素材网站一定要收藏好&#xff0c;可以有效帮你节省时间&#xff0c;提高工作效率。 1、菜鸟图库 https://www.sucai999.co…...

shell 脚本expect

expect 是什么 expect - programmed dialogue with interactive programs&#xff08;与互动程序进行程序对话&#xff09; 定义脚本执行的 shell #!/usr/bin/expect -f 定义的是执行 expect 可执行文件的链接路径&#xff08;或真实路径&#xff09;&#xff0c;功能类似于bas…...

第十九天 Maven总结

目录 Maven 1. 前言 2. 概述 2.1 介绍 2.2 安装 3. IDEA集成Maven 3.1 集成Maven环境 3.2 创建Maven项目 3.3 Maven坐标详解 3.4 导入maven项目 4. 依赖管理 4.1 依赖配置 4.2 依赖传递 4.3 依赖范围 4.4 生命周期 4.5 插件 Maven 1. 前言 1). 什么是Maven? …...

ESP8266-NodeMCU开发板-------开发板介绍(1)

目录 认识ESP8266-NodeMCU开发板​编辑 GPIO编号与NodeMCU开发板引脚名的区别&#xff1a; ESP8266 GPIO编号与NodeMCU开发板引脚名的对应关系 可用引脚 电压电流限制 特殊引脚情况说明 上拉电阻/下拉电阻 模拟输入 通讯 认识ESP8266-NodeMCU开发板 初识NodeMCU开发板 (第1章-第…...

【测试开发篇3】软件测试的常用概念

目录 一、软件测试的生命周期(5个步骤) ①需求分析(两个角度) 用户角度&#xff1a; 开发人员的角度&#xff1a; ②测试计划 ③测试设计、测试开发 ④执行测试 ⑤测试评估 二、软件测试贯穿项目的整个生命周期的体现 需求分析阶段 计划阶段 设计阶段 编码阶段 …...

javaEE初阶 — JavaScript WebAPI

文章目录什么是 DOMDOM 树获取元素1. querySelector2. querySelectorAll事件1. 事件三要素2. 代码案例获取 / 修改元素内容1. innerHTML获取 / 修改元素属性获取 / 修改表单元素属性获取 / 修改样式属性1. 修改内联样式&#xff08;修改 style 属性的值&#xff09;2. 修改元素…...

UE实现地面动态交互效果

文章目录 1.实现目标2.实现过程2.1 SphereMask2.2 材质实现2.3 位置更新3.参考资料1.实现目标 基于SphereMask材质节点实现人物在地面一定范围内的颜色高亮效果。 2.实现过程 实现原理是首先通过,SphereMask材质节点更具计算输出Mask值,其中在球体半径内的输入1,在外部的则…...

如何用自己的数据训练YOLOv5

如何训练YOLOv5 1. Clone the YOLOv5 repository and install dependencies: git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt2. 整理数据&#xff0c;使其适配YOLO训练 Step1&#xff1a;Organize your dataset in the fo…...

【基础算法】数组相关题目

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招算法的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于代码随想录进行的&#xff0c;每个算法代码参考leetcode高赞回答和…...

MatBox—基于PyQt快速入门matplotlib的教程库

MatBox—基于PyQt快速入门matplotlib的教程库 __ __ _ _ _ _ _ _ _______ _ _ _ | \/ | | | | | | | | |(_)| | |__ __| | | (_) | || \ / | __ _ |…...

go channel使用

go语言中有一句名言&#xff1a; 不要通过共享内存来通信&#xff0c;而应该通过通信来共享内存。 channel实现了协程间的互相通信。 目录 一、channel声明 二、向channel发送数据 三、从channel读取数据 1. i, ok : <-c 2. for i : range c&#xff08;常用&#xff09…...

5. QtDesignStudio中的3D场景

1. 说明&#xff1a; 三维渲染开发是Design Studio的重要功能&#xff0c;且操作方便&#xff0c;设计效率非常高&#xff0c;主要用到的控件是 View3D ,可以在3D窗口中用鼠标对模型直接进行旋转/移动/缩放等操作&#xff0c;也可以为模型设置各种动画&#xff0c;执行一系列的…...

人工智能的几个研究方向

人工智能主要研究内容是&#xff1a;分布式人工智能与多智能主体系统、人工思维模型、知识系统、知识发现与数据挖掘、遗传与演化计算、人工生命、人工智能应用等等。 其中热门研究有以下几种。 一、计算机视觉 就包括图像识别&#xff0c;视频识别&#xff0c;具体应用有人…...

软件测试 - 常见的开发模型和测试模型

1.瀑布模型优点强调开发的阶段性, 强调早期计划及需求调查, 强调产品测试;缺点1. 由于瀑布模型是一种线型结构的模型, 也就意味着前一个阶段结束, 后一个阶段才能开始, 这就导致了风险往往会迟至后期的测试阶段才显露, 因而失去了及早纠正的机会.2. 瀑布模型中测试被后置, 导致…...

从零开始的机械臂yolov5抓取gazebo仿真(四)

Moveit与Gazebo联合仿真 上一篇博客已经将moveit!配置完毕&#xff0c;然而想要让moveit!控制gazebo中的机械臂&#xff0c;还需要进行一些接口的配置。现在我们有的功能包为sunday_description、sunday_moveit_config这两个功能包。且已经配置好xacro文件&#xff0c;本篇内容…...

C++修炼之筑基期第一层——认识类与对象

文章目录&#x1f337;专栏导读&#x1f337;什么是面向对象&#xff1f;&#x1f337;类的引入&#x1f337;什么是类&#x1f337;类的定义方式&#x1f337;类的访问限定符与封装&#x1f33a;访问限定符&#x1f33a;封装&#x1f337;类的作用域&#x1f337;类的实例化&a…...

IT 运营监控工具

在技术复杂性日益增加、业务竞争激烈的挑战以及消费者对服务中断接受度降低的世界中&#xff0c;IT 运营效率已成为增长、利润和成功的关键。IT 宕机的影响在几十年前威胁较小&#xff0c;现在意味着价值数百万美元的损失&#xff0c;有时甚至会损失各种规模的组织的业务和声誉…...

java线程之Thread类的基本用法

Thread类的基本用法1. Thread类的构造方法2. Thread的几个常见属性常见属性线程中断等待一个线程小鱼在上一篇博客详细的讲解了如何创建线程,java使用Thread类来创建多线程,但是对于好多没有相关经验的人来说,比较不容易理解的地方在于操作系统调度的执行过程. 我们通过下面代码…...

【js】多分支语句练习(2)

个人名片&#xff1a; &#x1f60a;作者简介&#xff1a;一名大一在校生&#xff0c;web前端开发专业 &#x1f921; 个人主页&#xff1a;python学不会123 &#x1f43c;座右铭&#xff1a;懒惰受到的惩罚不仅仅是自己的失败&#xff0c;还有别人的成功。 &#x1f385;**学习…...

Redis与MySQL的双写一致性问题

Redis与MySQL的双写一致性问题更新缓存&#xff1f; 删除缓存&#xff1f;先更新缓存再更新数据库先更新数据库&#xff0c;再更新缓存先删除缓存再更新数据库先更新数据库&#xff0c;再删除缓存解决方案1. 重试2. 异步重试2.1 使用消息队列实现重试2.2 Binlog实现异步重试删除…...

Java基础:笔试题

文章目录Java 基础题目1. 如下代码输出什么&#xff1f;2. 当输入为2的时候返回值是多少?3. 如下代码输出值为多少?4. 给出一个排序好的数组&#xff1a;{1,2,2,3,4,5,6,7,8,9} 和一个数&#xff0c;求数组中连续元素的和等于所给数的子数组解析第一题第二题第三题第四题方案…...

spring三级缓存以及@Async产生循环引用

spring三级缓存以及Async产生循环引用spring三级缓存介绍三级缓存解除循环引用原理源码对应1、获取A&#xff0c;从三级缓存中获取&#xff0c;没有获取到2、构造A&#xff0c;将A置入三级缓存构造A(创建A实例)置入缓存3、注入属性&#xff0c;构造B扫描缓存实例的相关信息注入…...

【洛谷刷题】蓝桥杯专题突破-深度优先搜索-dfs(5)

目录 写在前面&#xff1a; 题目&#xff1a;P2036 [COCI2008-2009#2] PERKET - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 解题思路&#xff1a; 代码…...

【Unity3D】Unity3D中在创建完项目后自动创建文件夹列表

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 随着项目开发的体量增大&#xff0c;要导入大量的素材、UI、模…...

如何设计一个锂电池充电电路(TP4056)

这个是个单节18650锂电池的充电模块&#xff0c;这个是个18650的锂电池&#xff0c;18指的是它的直径是18mm&#xff0c;65指的是它的高度为65mm。这个18650电池的标称电压是3.7V&#xff0c;电池充满时电压为4.2V&#xff0c;一般电池电压越高也就代表它所剩的电量越大。这种锂…...