数据结构—队列(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.rhkb.cn/news/333905.html

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

相关文章

五分钟搭建一个Suno AI音乐站点

五分钟搭建一个Suno AI音乐站点 在这个数字化时代&#xff0c;人工智能技术正以惊人的速度改变着我们的生活方式和创造方式。音乐作为一种最直接、最感性的艺术形式&#xff0c;自然也成为了人工智能技术的应用场景之一。今天&#xff0c;我们将以Vue和Node.js为基础&#xff…

MySQL触发器实战:自动执行的秘密

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 MySQL触发器实战&#xff1a;自动执行的秘密 前言触发器的定义和作用触发器的定义和作用触发器的…

leetCode.82. 删除排序链表中的重复元素 II

leetCode.82. 删除排序链表中的重复元素 II 题目思路&#xff1a; 代码 class Solution { public:ListNode* deleteDuplicates(ListNode* head) {auto dummy new ListNode(-1);dummy->next head;auto p dummy;while(p->next){auto q p->next->next;while(q …

插件“猫抓”使用方法 - 浏览器下载m3u8视频 - 合并 - 视频检测下载 - 网课下载神器

前言 浏览器下载m3u8视频 - 合并 - 网课下载神器 chrome插件-猫抓 https://chrome.zzzmh.cn/info/jfedfbgedapdagkghmgibemcoggfppbb 步骤&#xff1a; P.s. 推荐大佬的学习视频&#xff01; 《WEB前端大师课》超级棒&#xff01; https://ke.qq.com/course/5892689#term_id…

使用Python操作Jenkins

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

C语言中的位段

位段是通过结构体实现的&#xff0c;可以在一定程度上减小空间浪费&#xff0c;位段的声明和结构体类似&#xff0c;有以下几个不同&#xff1a; ①位段的成员必须是整形&#xff08;int,char,short等&#xff09;。 ②成员后边有冒号和数字&#xff0c;表示该成员占几个bit位…

【译】MySQL复制入门: 探索不同类型的MySQL复制解决方案

原文地址&#xff1a;An Introduction to MySQL Replication: Exploring Different Types of MySQL Replication Solutions 在这篇博文中&#xff0c;我将深入介绍 MySQL 复制&#xff0c;回答它是什么、如何工作、它的优势和挑战&#xff0c;并回顾作为 MySQL 环境&#xff0…

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

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

深度神经网络——贝叶斯与朴素贝叶斯定理

概述 贝叶斯定理是概率论中一个非常重要的概念&#xff0c;它提供了一种在已知某些相关事件的概率时&#xff0c;计算另一个事件发生概率的方法。在你提供的内容中&#xff0c;贝叶斯定理被描述为一种“魔法”&#xff0c;因为它能够使计算机通过分析大量的数据来预测人们可能…

十四天学会Vue——Vue核心(理论+实战)中篇(第二天)

声明&#xff1a;是接着上篇讲的哦&#xff0c;感兴趣可以去看一看~ 这里一些代码就不写了&#xff0c;为了缩减代码量&#xff0c;大家知道就可以了&#xff1a; Vue.config.productionTip false //阻止 vue 在启动时生成生产提示。热身小tips&#xff0c;可以安装这个插件&…

【LeetCode】【9】回文数(1047字)

文章目录 [toc]题目描述样例输入输出与解释样例1样例2样例3 提示进阶Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;LeetCode 刷题指南&#xff1a;LeetCode刷题指南 题目描述 给一个整数x&#xff0c;如果x是一个回文整数&#xff0c;返回true&#xff1b;否…

MIT6.828 Lab2-1 Using gdb

Using gdb gdb使用&#xff1a; xv6 gdb调试方法 问题1&#xff1a; Looking at the backtrace output, which function called syscall? 按照提示开启gdb后键入&#xff1a; b syscall c layout src backtrace输出结果&#xff1a; (gdb) backtrace #0 syscall () at k…

Python + adb 实现打电话功能

前言 其实很多年前写过一篇python打电话的功能&#xff0c;链接如下&#xff1a; Python twilio 实现打电话和发短信功能_自动发短信代码-CSDN博客 今天由于工作需要&#xff0c;又用python写了个关于打电话的小工具&#xff0c;主要是通过ADB方式实现的 实现过程 1.先利用…

YOLOv8+PyQt5鸟类检测系统完整资源集合(yolov8模型,从图像、视频和摄像头三种路径识别检测,包含登陆页面、注册页面和检测页面)

资源包含可视化的鸟类检测系统&#xff0c;基于最新的YOLOv8训练的鸟类检测模型&#xff0c;和基于PyQt5制作的可视化鸟类检测系统&#xff0c;包含登陆页面、注册页面和检测页面&#xff0c;该系统可自动检测和识别图片或视频当中出现的各种鸟类&#xff0c;以及自动开启摄像头…

Putty: 随心御剑——远程启动服务工具plink

一、引言:如何远程控制 也许你会有这样的场景,交互程序(以下简称UI程序)跑在windows端,而控制程序跑在Linux上。我们想要通过windows端 UI程序来启动Linux下面的服务,来一场酣畅淋漓的御剑飞行咋办,难道要自己十年磨一剑,在Linux下编写一个受控服务程序么.计算机科技发…

如何创建一个vue项目?详细教程,如何创建第一个vue项目?

已经安装node.js在自己找的到的地方新建一个文件夹用于存放项目&#xff0c;记住文件夹的存放路径&#xff0c;以我为例&#xff0c;我的文件夹路径为D:\tydic 打开cmd命令窗口&#xff0c;进入刚刚的新建文件夹 切换硬盘&#xff1a; D: 进入文件夹&#xff1a;cd tydic 使…

重学java 49 List接口

但逢良辰&#xff0c;顺颂时宜 —— 24.5.28 一、List接口 1.概述: 是collection接口的子接口 2.常见的实现类: ArrayList LinkedList Vector 二、List集合下的实现类 1.ArrayList集合的使用及源码分析 1.概述 ArrayList是List接口的实现类 2.特点 a.元素有序 —> 按照什么顺…

红外成像人员检测数据集VOC+YOLO格式5838张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;5838 标注数量(xml文件个数)&#xff1a;5838 标注数量(txt文件个数)&#xff1a;5838 标注…

UE5 CommonUI的使用(附源码版)

UE5 CommonUI的使用 前言快速配置配置Game Viewport Client ClassCommonGameViewportClient源代码 创建CommonInputAction表默认导航Action设置CommonUIInputData源码 Bind CommonInputBaseControllerDataCommonInputBaseControllerData源码 Common UI控件库和控件样式CommonUs…

探索Python编程乐趣:制作气泡反弹小游戏

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;Python编程的轻松入门 二、游戏实现原理&#xff1a;气泡反弹的逻辑 …