数据结构—队列(C语言实现)

文章目录

  • 前言
  • 一、队列的概念
  • 二、队列的实现
    • Queue.h
    • Queue.c
  • 三、设计循环队列问题
    • 数组实现
    • 链表实现
  • 总结


前言

嗨喽喽!!小伙伴们,大家好哇,欢迎来到我的博客!
在这里插入图片描述

今天将要分享的是另一种数据结构—队列,以及与队列具有相通之处的一种结构,循环队列的实现。

一、队列的概念

队列(Queue):只能在一端进行插入数据操作,在另一端进行删除数据的特殊线性表。与栈相反的是,队列遵循先进先出 FIFO(First In First Out)的原则,进行插入数据的一端称之为队尾(入队),进行删除数据的一端则为队头(出队)。
在这里插入图片描述
看到队列这一数据结构的名字与以上对于队列的概念的说明,相信小伙伴们脑海中早已浮现日常生活中与此结构相类似的场景。就是我们平时购买物品或办理业务时,通常会采取排队的方式确保秩序与公平,先入队的人先出队。
在这里插入图片描述

二、队列的实现

讲述完了队列的相关概念,接下来便可以着手实现队列及其有关的基本操作了!!
队列既可以使用数组也可以使用链表来实现。但是总体来说,使用链表实现更优,因为使用数组在队头出数据,其效率会低很多

所以我们使用链表来实现一个队列:

Queue.h

首先时队列头文件的头文件包含与链表和队列的声明:

typedef int QDataType;typedef struct QListNode
{struct QListNode* _pNext;//指向队列的下一个QDataType _data;
}QNode;typedef struct Queue
{QNode* _front;//指向队头QNode* _rear;//指向队尾int _size;
}Queue;

然后是,队列中的一些相关的方法的声明:

//初始化队列
void QueueInit(Queue* q);
//销毁队列
void QueueDestroy(Queue* q);//入队(队尾)
void QueuePush(Queue* q, QDataType x);
//出队(队头)
void QueuePop(Queue* q);//获取队头元素
QDataType QueueFront(Queue* q);
//获取队尾元素
QDataType QueueBack(Queue* q);//队列判空
bool QueueEmpty(Queue* q);
//获取队列元素个数
int QueueSize(Queue* q);

Queue.c

接下来便是队列中的一些方法的实现:
首先是队列的初始化与销毁,销毁操作类似于链表,通过遍历对节点逐个释放:

void QueueInit(Queue* q)
{assert(q);q->_front = q->_rear = NULL;q->_size = 0;
}void QueueDestroy(Queue* q)
{assert(q);QNode* pcur, * next;pcur = q->_front;while (pcur){next = pcur->_pNext;free(pcur);pcur = next;}q->_front = q->_rear = NULL;q->_size = 0;
}

然后是队列的入队操作,在入队之前需要先检查队列是否为空

void QueuePush(Queue* q, QDataType x)
{assert(q);QNode* node = (QNode*)malloc(sizeof(QNode));if (node == NULL){perror("malloc fail!");exit(1);}node->_data = x;node->_pNext = NULL;if (q->_rear)//队列不为空{q->_rear->_pNext = node;}else//队列为空{q->_front = node;}q->_rear = node;q->_size++;
}

使用动画解释入队操作:
在这里插入图片描述
然后是出队操作,此处则存在队列为一个元素多个元素的情况:

void QueuePop(Queue* q)
{assert(q && q->_size);if (q->_front->_pNext)//队列不仅一个元素{QNode* next = q->_front->_pNext;free(q->_front);q->_front = NULL;q->_front = next;}else//队列仅一个元素{free(q->_front);q->_front = q->_rear = NULL;}q->_size--;
}

使用动画解释出队操作:
在这里插入图片描述
然后是获取队头与队尾元素:

QDataType QueueFront(Queue* q)
{assert(q && q->_front);return q->_front->_data;
}QDataType QueueBack(Queue* q)
{assert(q && q->_rear);return q->_rear->_data;
}

最后则是队列的判空与获取当前队列的大小的操作:

bool QueueEmpty(Queue* q)
{assert(q);return q->_size == 0;
}int QueueSize(Queue* q)
{assert(q);return q->_size;
}

三、设计循环队列问题

分享完了队列的相关内容,接下来让我们看一个与队列有一定相通之处的设计循环队列的问题。
首先给出力扣上的相关题目的链接:【设计循环队列】。
在这里插入图片描述
当然,这个循环队列既可以使用数组也可以使用链表来实现,由于难度基本相差不大。这边主要使用数组来讲解实现过程,同时在最后也会给出使用链表实现的相关代码。

数组实现

首先通过题目我们可以知道循环队列的逻辑结构大致类似于下图:
在这里插入图片描述
但是他的物理结构上确实一个数组,不可能将首尾相接。那么这里我们便可以通过对tail用size取模。当tail在数组末尾时再加一,那么肯定会造成数组越界,此时我们就可以使用size对tail进行取模,tail便成了0,指向了数组首位,如此便形成了循环,当然队头也可以使用相类似的操作。如下图所示,tail开始为5,size为6,此时队列入数据,对tail加1,我们对tail取模,tail就等于了0:
在这里插入图片描述
但此时,我们会存在两种特殊情况,那便是数组的空与满。数组为空还可以放入元素,为满那肯定不可以再存入元素。这时,可能部分小伙伴会说,判断此时队头与队尾指向的位置是否相同,来区分空与满。但要知道的是空与满时队头与队尾指向的位置其实都是相同的。

其实此时我们可以采取两种方法来解决上述的问题:
1、使用一个size来记录当前的数组元素的个数,当元素的个数与循环队列的长度k相等时,不就为满了,size==0不就为空了。

2、我们可以实际上创建k+1个长度循环队列,然后让tail一直指向末尾元素的下一个下标的位置,即让数组中始终有一个位置不存放数据。此时队头与队尾指向同一个位置时,即为空;队尾的下一个为队头就为满:
在这里插入图片描述

那我们就使用第一种方法来手撕一个循环队列吧!!
首先是循环队列的声明:

typedef struct {int* a;int head;int tail;int size;//判断满与空的特殊情况int k;
} MyCircularQueue;

然后是循环队列的初始化,一开始头与尾都指向数组的开始位置:

MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* pcq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));pcq->a = (int*)malloc(k * sizeof(int));pcq->size = pcq->head = pcq->tail = 0;pcq->k = k;return pcq;
}

循环队列的入队操作,入队之前需要先判断队列是否为满,即size与k相等时即为满,然后在对tial加加了之后,则需要使用k对tail取模,形成循环:

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (obj->size == obj->k)//循环队列为满{return false;}obj->a[obj->tail] = value;obj->tail++;obj->tail %= obj->k;obj->size++;return true;
}

循环队列的出队操作,出队之前则需要判断队列是否为空(即size==0),不为空,就直接让head++。同样的,在对head++后也需要使用k对head进行取模操作:

bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (obj->size == 0)//循环队列为空{return false;}obj->head++;obj->head %= obj->k;obj->size--;return true;
}

然后是循环队列的取队头数据的操作,直接返回head指向的数据即可:

int myCircularQueueFront(MyCircularQueue* obj) {if (obj->size == 0)return -1;return obj->a[obj->head];
}

取循环队列的尾数据就相对复杂了,因为tail的前一个元素为尾元素。那么,当tail==0时,尾元素在数组的末尾位置。当然,有一种简单的解决方法:使用三目运算符,即判断tail是否指向0,是就返回数组末尾元素(即k - 1的位置),否就只返回tail - 1指向的元素即可。
此处同时还有一种更为🐂🍺(NB)的写法,就是返回使用k对tail取模位置的元素。即让tail - 1 + k在使用k对结果进行取模操作,此时的值便指向了循环队列的末尾元素。比如tail为0时,减1加k再用k取模,值为k - 1:

int myCircularQueueRear(MyCircularQueue* obj) {if (obj->size == 0)return -1;return obj->tail == 0 ? obj->a[obj->k - 1] : obj->a[obj->tail - 1];//return obj->a[(obj->tail - 1 + obj->k) % obj->k];//更装逼的写法
}

接下来就是循环队列的判空与判满操作,非常简单,就不加赘述:

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->size == 0;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return obj->size == obj->k;
}

最后则是循环队列的销毁操作:

void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);obj->a = NULL;obj->size = obj->k = obj->head = obj->tail = 0;free(obj);
}

代码写完,直接提交完事:
在这里插入图片描述

链表实现

最后再给出使用链表实现循环队列,并采用对循环队列多创建一个位置的方法进行判断循环队列的空与满的代码。

使用链表是实现就不需要取模来让队列循环了,只需要让链表的首节点与尾节点相连接形成循环链表即可。基本实现逻辑大致相同,就是判空与判满操作存在不同,这里直接上代码:

typedef struct LTNode
{int x;struct LTNode* next;
}LTNode;//用链表实现
typedef struct {LTNode* phead;LTNode* ptail;int k;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->phead = (LTNode*)malloc(sizeof(LTNode));obj->ptail = obj->phead;for (int i = 0; i < k; i++)//多创建一个节点判断空与满的情况{obj->ptail->next = (LTNode*)malloc(sizeof(LTNode));obj->ptail = obj->ptail->next;}obj->ptail->next = obj->phead;obj->ptail = obj->phead;obj->k = k + 1;return obj;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {//循环队列头节点与尾节点相同时,队列为空return obj->phead == obj->ptail;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {//循环队列尾节点的下一个节点为头节点时,队列为满return obj->ptail->next == obj->phead;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj))return false;obj->ptail->x = value;obj->ptail = obj->ptail->next;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return false;obj->phead = obj->phead->next;return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;return obj->phead->x;
}int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;LTNode* tail = obj->phead;while (tail->next != obj->ptail)tail = tail->next;return tail->x;
}void myCircularQueueFree(MyCircularQueue* obj) {LTNode* pcur = obj->phead;for (int i = 0; i < obj->k; i++){LTNode* next = pcur->next;free(pcur);pcur = NULL;pcur = next;}free(obj);
}

最后提交走人:
在这里插入图片描述

总结

以上便是有关队列的相关知识的分享。如果,小伙伴们觉得有帮助的话,点个赞是最好的!ο(=•ω<=)ρ⌒☆

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

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

相关文章

汇编原理(三)编程

源程序&#xff1a; 汇编指令&#xff1a;有对应的机器码与其对应 伪指令&#xff1a;无对应的机器码&#xff0c;是由编译器来执行的指令&#xff0c;编译器根据伪指令来进行相关的编译工作。 ex1:XXX segment、XXX ends这两个是一对成对使用的伪指令&#xff0c;且必须会被用…

打工人好用的大模型问答,还需要一款可靠的文档解析工具

如果说三四年前&#xff0c;我们对AI的展望还停留在科幻片的话&#xff0c;现在&#xff0c;通向AI智能的路径已经初现端倪。各行各业的朋友们不约而同地嗅到了大模型带来的生产方式变革气息。 LLM宣布了AI时代的正式到来。 2022年11月30日&#xff0c;ChatGPT发布&#xff0…

“智能体时代:探索无限可能——零代码构建智能教练智能体“

随着智能体技术的飞速发展&#xff0c;各个领域正经历着空前的变革和新的发展机遇。作为人工智能的一个关键组成部分&#xff0c;智能体以其自我驱动、智能响应和适应能力&#xff0c;逐渐深入到我们日常生活的各个层面&#xff0c;成为促进社会发展和科技进步的新引擎。 顺应这…

30V MOS管 60VMOS管 100VMOS管 150VMOS管推荐

MOS管&#xff0c;即金属氧化物半导体场效应管&#xff0c;其工作原理是&#xff1a;在P型半导体与N型半导体之间形成PN结&#xff0c;当加在MOS管栅极上的电压改变时&#xff0c;PN结之间的沟道内载流子的数量会随之改变&#xff0c;沟道电阻也会发生改变&#xff0c;进而改变…

【JavaEE精炼宝库】多线程(3)线程安全 | synchronized

目录 一、线程安全 1.1 经典线程不安全案例&#xff1a; 1.2 线程安全的概念&#xff1a; 1.3 线程不安全的原因&#xff1a; 1.3.1 案例刨析: 1.3.2 线程不安全的名词解释&#xff1a; 1.3.3 Java 内存模型 (JMM)&#xff1a; 1.3.4 解决线程不安全问题&#xff1a; 二…

工业AI的崛起,中国自主创新的新机遇

我们都知道&#xff0c;互联网已经深刻地改变了我们的生活方式&#xff0c;催生了无数的新型商业模式和创新产业&#xff0c;推动了社会的经济变革。中国在互联网领域的发展取得了举世瞩目的成就&#xff0c;建成了全球规模最大、技术领先的5G网络&#xff0c;互联网应用的普及…

linux文件编程api: creat

1.基本信息 功能 创建新文件 头文件 #include<fcntl.h> 函数形式 int creat(const char *pathname, mode_t mode); 返回值 如果成功&#xff0c;则返回文件描述符号 如果失败&#xff0c;则返回-1 参数 pathname: 创建的文件名 mode: 新建文件时&#xff0c;文件权限…

Django革新者:突破传统,构建下一代Web应用

书接上文 —— 家园建筑师&#xff1a;用Django打造你的Web帝国&#xff0c;从前面的学习中&#xff0c;咱们我们经历了一个完整的Django Web开发之旅&#xff0c;涵盖了从基础概念到高级特性的各个方面&#xff1a; 引言&#xff1a;介绍了企业级Web框架的需求&#xff0c;并概…

牛客NC67 汉诺塔问题【中等 递归 Java/Go/PHP/C++】 lintcode 169 · 汉诺塔

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/7d6cab7d435048c4b05251bf44e9f185 https://www.lintcode.com/problem/169/ 思路 相传在古印度圣庙中&#xff0c;有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上&#xff0c;有三根杆(编号A、B、C…

使用Python操作Jenkins

大家好&#xff0c;Python作为一种简洁、灵活且功能丰富的编程语言&#xff0c;可以与各种API轻松集成&#xff0c;Jenkins的API也不例外。借助于Python中的python-jenkins模块&#xff0c;我们可以轻松地编写脚本来连接到Jenkins服务器&#xff0c;并执行各种操作&#xff0c;…

拌合楼系统开发(二十)解决海康DS-TVL224系列屏幕显示二维码思路

前言&#xff1a; 需求是想在通过程序动态控制显示屏显示二维码&#xff0c;最开始有些担心led这种点阵屏会不会对二维码显示出来后无法识别&#xff0c;实际测时候发现是没问题的。对于显示文字和语音播报&#xff0c;csdn上已经有大神有完整的代码。 海康威视道闸进出口LED屏…

100个 Unity小游戏系列三 -Unity 抽奖游戏专题二 水果机游戏

一、演示效果 二、知识点 2.1 布局 private void CreateItems(){for (int i 0; i < rewardDatas.Length; i){var reward_data rewardDatas[i];GameObject fruitOjb;if (i < itemRoot.childCount){fruitOjb itemRoot.GetChild(i).gameObject;}else{fruitOjb Instant…

数据中台建设方案(Word版源文档)

建设大数据管理中台&#xff0c;按照统一的数据规范和标准体系&#xff0c;构建统一数据采集&#xfe63;治理&#xfe63;共享标准、统一技术开发体系、统一接口 API &#xff0c;实现数据采集、平台治理&#xff0c;业务应用三层解耦&#xff0c;并按照统一标准格式提供高效的…

最新!!2024年上半年软考【中级软件设计师】综合知识真题解析

2024上半年软考考试已经结束了&#xff0c;为大家整理了网友回忆版的软件设计师真题及答案&#xff0c;总共30道题。 上半年考试的宝子们可以对答案预估分数&#xff01;准备下半年考的宝子可以提前把握考试知识点和出题方向&#xff0c;说不定会遇到相同考点的题目&#xff01…

[集群聊天服务器]----(十)Nginx的tcp负载均衡配置--附带截图

接着上文&#xff0c;我们剖析了服务端和客户端的代码&#xff0c;但是单台服务器的并发量是有限的&#xff0c;面对并发量的要求&#xff0c;我们就需要引入Nginx来实现并发量的要求&#xff0c;将用户请求分发到不同的服务器上分担压力&#xff0c;这就是负载均衡。 选择负…

汽车制造业安全有效的设计图纸文件外发系统是什么样的?

在汽车制造的世界里&#xff0c;那些设计图不仅仅是公司智慧的闪光点&#xff0c;更是它们竞争的秘密武器。但问题来了&#xff0c;当公司需要和供应商、合作伙伴频繁交换数据时&#xff0c;怎样安全又高效地发送这些设计图&#xff0c;就成了一个头疼的问题。这篇文章会深挖一…

计算机网络——在地址栏输入网址(URL)之后都发生了什么

网址&#xff0c;也叫域名&#xff0c;域名就像一个 IP 地址的可读版本&#xff0c;比如&#xff0c;百度的域名 www.baidu.com&#xff0c;他的 ip 是 110.242.68.3&#xff0c;输入 IP 一样可以跳转到百度搜索的页面&#xff0c;我想没有一个人没去记百度的 IP 吧。其实我们真…

部署Prometheus + Grafana实现监控数据指标

1.1 Prometheus安装部署 Prometheus监控服务 主机名IP地址系统配置作用Prometheus192.168.110.27/24CentOS 7.94颗CPU 8G内存 100G硬盘Prometheus服务器grafana192.168.110.28/24CentOS 7.94颗CPU 8G内存 100G硬盘grafana服务器 监控机器 主机名IP地址系统配置k8s-master-0…

符合车规级漏电流检测的磁通门传感器KTD1100

电动车充电桩 在政策出台后&#xff0c;充电桩类产品按要求需装配B端漏电流检测装置。它可以有效防止充电桩等设备中的漏电流对用户造成危害&#xff0c;保障用户的用电安全。其次&#xff0c;它可以促进充电桩等产品的质量提升&#xff0c;提高市场的公平竞争&#xff0c;让消…

无线领夹麦克风哪个品牌好?本期文章揭秘无线麦克风哪个品牌好用

​在当下这个全民皆为媒体的时代大潮中&#xff0c;视频分享已然成为了引领风尚的指向标。在自媒体领域竞争愈发激烈的态势下&#xff0c;若要在这片广阔海洋中扬帆远航&#xff0c;优秀的作品毫无疑问是吸引观众的关键所在。而想要塑造出这样的卓越之作&#xff0c;除了需要创…