数据结构—队列(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,一经查实,立即删除!

相关文章

Transformer详解(5)-编码器

Transformer编码器 import copy import torch from torch import nn from norm import Norm from multi_head_attention import MultiHeadAttention from feed_forward import FeedForward from pos_encoder import PositionalEncoderdef get_clones(module, N):""&…

低空经济蓬勃发展

我国低空经济蓄势起飞从2021年2月首次将“低空经济”写入国家规划&#xff0c;到2023年12月正式将低空经济定位为战略性新兴产业&#xff0c;国家层面鼓励并积极推动低空经济发展。今年政府工作报告提出&#xff0c;积极打造生物制造、商业航天、低空经济等新增长引擎。截至3月…

C 语言设计模式(创建型)

文章目录 工厂模式场景使用结构体示例使用函数指针示例线程安全示例 单例模式场景示例线程安全示例 建造者模式场景示例 原型模式场景示例 工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种创建对象的最佳方式&#xf…

汇编原理(三)编程

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

thingsboard 仪表盘部件如何解析设备RCP应答

对设备添加仪表部件Switch control后&#xff0c;开关部件默认会发送getValue v1/devices/me/rpc/request/0 {"method":"getValue","params":null} 此时设备收到请求后回复一个“value”值&#xff0c;“value”可以是任何字符串 v1/devices/…

spark机器学习之协同过滤

协同过滤算法 协同过滤是一类基于用户行为数据的推荐算法,它的核心思想是利用用户的历史行为数据(比如评分、购买、点击等)来发现用户之间的相似性或者物品之间的相似性,从而给用户推荐他们可能感兴趣的物品。协同过滤算法通常分为两种类型:基于用户的协同过滤和基于物品…

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

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

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

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

Linux 文件权限管理详解

查看文件权限 使用ls -l命令&#xff1a; 打开终端&#xff0c;进入文件所在的目录&#xff0c;然后输入以下命令查看文件或目录的详细信息&#xff0c;包括权限、所有者、所属组、大小、修改日期等&#xff1a; ls -l 文件或目录名 示例&#xff1a; ls -l myfile.txt ls -l m…

力扣刷题--2951. 找出峰值【简单】

题目描述 给你一个下标从 0 开始的数组 mountain 。你的任务是找出数组 mountain 中的所有 峰值。 以数组形式返回给定数组中 峰值 的下标&#xff0c;顺序不限 。 注意&#xff1a; 峰值 是指一个严格大于其相邻元素的元素。 数组的第一个和最后一个元素 不 是峰值。 示例…

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; 二…

内存池的实现

概述&#xff1a;本文介绍用户层内存池的实现 Q:为什么需要内存池&#xff1f; A&#xff1a;在项目中&#xff0c;用户层通过malloc系统调用申请内存的次数可能很多&#xff0c;每一次malloc系统调用&#xff0c;都会引起用户态——内核态的切换&#xff0c;这样的开销对性能…

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

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

各种网络协议在设计目的、工作方式、应用场景等方面存在显著的区别

各种网络协议在设计目的、工作方式、应用场景等方面存在显著的区别。以下是一些常见网络协议的区别 概述&#xff1a; TCP与UDP&#xff1a; 设计目的&#xff1a;TCP&#xff08;传输控制协议&#xff09;提供面向连接的、可靠的、基于字节流的传输服务。UDP&#xff08;用户…

linux文件编程api: creat

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

Java Web(入门)

Java Web 1. 入门基础 1.1 Java Web简介 Java Web开发是指使用Java技术来创建动态网站或Web应用程序。Java Web开发主要使用Servlet、JSP&#xff08;JavaServer Pages&#xff09;、JavaBeans等技术来实现动态页面和处理业务逻辑。 1.2 环境搭建 为了开发Java Web应用程序…

TypeScript-字面量类型

字面量类型 使用 JS字面量 作为类型对变量进行类型注解&#xff0c;这种类型就是字面量类型&#xff0c;字面量类型比普通的类型更加精确 // 普通number类型&#xff0c;可以赋值任何数值 let count: number count 1 count 2 // 字面量类型100 只能赋值为100 let count: 1…

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…