探索数据结构:链式队与循环队列的模拟、实现与应用

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法
贝蒂的主页:Betty’s blog

1. 队列的定义

队列(queue)是一种只允许在一端进行插入操作,而在另一端进行删除操作的线性表。其严格遵循先进先出(First In First Out)的规则,简称FIFO

img

img

  • 队头(Front):允许删除的一端,又称队首。
  • 队尾(Rear):允许插入的一端。

2. 队列的分类

队列与栈类似,实现方式有两种。一种是以数组的方式实现,另一种以单链表来实现。这两种实现方式各有优劣,并且都有细节需要处理。

  1. 基于单链表实现:我们可以将链表的头节点与尾节点分别作为队列的队首与队尾,这样我们就能用两个指针来对其进行操作。如下图:

img

  1. 基于数组实现:我们同样可以通过两个下标分别指向数组的起始与结束,但这时我们就可能发现两个问题:
  • 问题一:在不断出队与进队得到过程中,起始下标与末尾下标都在向后移动,当两个下标同时指向数组末尾时就无法再移动了,并且**浪费前面大量空间,**如图1

  • 问题二:为了解决上述问题,我们将数组首尾相接变为循环数组。但这时又会出现一个问题,那便是当队首与队尾下标指向同一个节点时,这个队列到底是还是呢?这时我们有三个解决方法

  • 第一种:牺牲一个单元来区分队空和队满,这时若队列不为空,让队尾下标指向队尾的下一个位置。约定以队头指针在队尾指针的下一位置作为队满的标志,即Q->rear+1==Q->front。如图二。

  • 第二种:增设表示元素个数的数据成员 size 。这样,队空的条件为 Q->size==0;队满的条件为 Q->size==MaxSize

  • 第三种:增加表示队满的数据成员flag。将flag初始化为0,当队满时将其置为1。

img

3. 队列的功能

  1. 队列的初始化。
  2. 判断队列是否为空。。
  3. 返回队头与队尾的元素。
  4. 返回队列的大小。
  5. 入队与出队。
  6. 打印队列的元素。
  7. 销毁队列。

4. 队列的声明

4.1. 链式队

链式队的声明十分简单,参照上面图我们就可以直接实现了。

typedef int QDataType;
typedef struct QueueNode 
{QDataType data;struct QueueNode* next;
}QNode;
typedef struct Queue 
{QNode* front;QNode* rear;size_t size;
}Queue;

4.2. 循环队

根据上述分析,我们采用一个数组来实现队列,其声明如下

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDataType;
#define MAXSIZE 50  //定义元素的最大个数
/*循环队列的顺序存储结构*/
typedef struct {QDataType data[MAXSIZE];int front;  //头指针int rear;   //尾指针
}Queue;void QueueInit(Queue* q);//初始化队列
bool QueueEmpty(Queue* q);//判断是否为空
QDataType QueueFront(Queue* q);//获取队头元素
QDataType QueueBack(Queue* q);//或许队尾元素
size_t QueueSize(Queue* q);//或许队列长度
void QueuePush(Queue* q, QDataType x);//入队
void QueuePop(Queue* q);//出队
void QueuePrint(Queue* q);//打印队列元素

5. 队列的初始化

对队列声明的数据进行初始化,防止随机值。

5.1. 链式队

void QueueInit(Queue* q)
{q->front = NULL;q->rear = NULL;q->size = 0;
}

5.2. 循环队

void QueueInit(Queue* q) 
{q->front = 0;q->rear = 0;
}

5.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)。

6. 判断队列是否为空

判断队列是否为空十分简单,这里就不在赘述。

6.1. 链式队

bool QueueEmpty(Queue* q)
{assert(q);return (q->front == NULL) && (q->rear == NULL);
}

6.2. 循环队

bool QueueEmpty(Queue*q)
{assert(q);return q->front == q->rear;
}

6.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)。

7. 返回队头与队尾元素

因为定义了头指针与尾指针,所以访问数据也十分方便。

7.1. 链式队

QDataType QueueFront(Queue* q)
{assert(q);assert(!QueueEmpty(q));return q->front->data;
}
QDataType QueueBack(Queue* q)
{assert(q);assert(!QueueEmpty(q));return q->rear->data;
}

7.2. 循环队

QDataType QueueFront(Queue* q)
{assert(q);assert(!QueueEmpty(q));return q->data[q->front];
}
QDataType QueueBack(Queue* q)
{assert(q);assert(!QueueEmpty(q));return q->data[q->rear-1];
}

7.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)。

8. 队列的大小

8.1. 链式队

size_t QueueSize(Queue* q)
{return q->size;
}

8.2. 循环队

求循环队列的大小,我们很容易想到用Q->rear-Q->front得出队列元素个数。但是我们要考虑到一种特殊情况:当队列先删除元素再添加元素时,末尾下标**rear**可能循环重置,如下图。

img

那到底该如何解决这个问题呢?其实我们只需要在原来基础上加上一个MAXSIZE就行了,为了使图一情况也适用我们仍需模上一个MAXSIZE

size_t QueueSize(Queue*q) 
{assert(q);return (q->rear - q->front + MAXSIZE) % MAXSIZE;
}

8.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)。

9. 入队

9.1. 链式队

链式队列入队时需要判断队列是否为空的特殊情况,如果是则还需要将尾指针也指向这个节点。

void QueuePush(Queue* q, QDataType x)
{assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));newnode->data = x;newnode->next = NULL;if (newnode == NULL){perror("malloc fail");return;}if (q->front == NULL){q->front = q->rear = newnode;}else{q->rear->next = newnode;q->rear = newnode;}q->size++;
}

9.2. 循环队

为了使循环队列在插入数据时实现循环操作,我们可以每次进行取模操作。

void QueuePush(Queue* q, QDataType x) 
{assert(q);q->data[q->rear] = x;   q->rear = (q->rear + 1) % MAXSIZE;  //rear指针向后移一位置,若到最后则转到数组头部
}

9.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)。

10. 出队

10.1. 链式队

同样考虑特殊情况,防止队列为空。并且当队列只有一个节点时需要将头指针与尾指针都置为空。

void QueuePop(Queue* q)
{assert(q);assert(!QueueEmpty(q));//1.只有一个结点if (q->front == q->rear){free(q->front);q->front = q->rear = NULL;}//2.有多个结点else{QNode* del = q->front;q->front = q->front->next;free(del);del = NULL;}q->size--;
}

10.2. 循环队

同样为了实现循环,我们可以进行取模操作。

 void QueuePop(Queue* q){assert(q);assert(!QueueEmpty(q));q->front = (q->front + 1) % MAXSIZE;    //front指针向后移一位置,若到最后则转到数组头部}

10.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)。

11. 打印队列

11.1. 链式队

void QueuePrint(Queue* q)
{assert(q);QNode* cur = q->front;QNode* tail = q->rear;printf("队头->");while (cur != tail->next){printf("%d->",cur->data);cur = cur->next;}printf("队尾\n");
}

11.2. 循环队

 void QueuePrint(Queue* q){assert(q);int cur = q->front;printf("队头->");while (cur != q->rear){printf("%d->", q->data[cur]);cur = (cur + 1) % MAXSIZE;}printf("队尾\n");}

11.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:无论是链式队还是循环队列花费空间都是一个固定大小,所以空间复杂度为O(1)。

12. 销毁队列

12.1. 链式队

void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->front;while (cur){QNode* del = cur;cur = cur->next;free(del);del = NULL;}q->front = q->rear = NULL;
}

12.2. 循环队

循环队列是以数组作为存储空间,并不是动态内存开辟的空间,所以并不需要手动释放空间。

12.3. 复杂度分析

  • 时间复杂度:无论是链式队还是循环队列花费时间都是一个常数,所以时间复杂度为O(1)。
  • 空间复杂度:链式队花费空间都是一个固定大小,所以空间复杂度为O(1)。

13. 链式队与循环队列的对比与应用

13.1. 对比

对比项链式队循环队列
时间效率因为存在头指针与尾指针,所以链式队的出队与入队的时间都相对较小。循环队列是基于数组实现的,支持下标的随机访问,所以时间消耗也并不大
空间效率链式队每次入队都需固定创造一个新的节点,空间利用率较高,较稳定。循环队列的空间是固定的,可能会造成空间的浪费。

13.2. 应用

队列的应用与栈一样,十分广泛

  1. 当我们去食堂扫码订餐时,你的订单就会加入一个队列中。
  2. 在操作系统中,队列可以用来管理任务进度与进程切换。

14. 完整代码

14.1. 链式队

14.1.1. Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDataType;
typedef struct QueueNode 
{QDataType data;struct QueueNode* next;
}QNode;
typedef struct Queue 
{QNode* front;QNode* rear;size_t size;
}Queue;
void QueueInit(Queue* q);//初始化队列
bool QueueEmpty(Queue* q);//判断是否为空
QDataType QueueFront(Queue* q);//获取队头元素
QDataType QueueBack(Queue* q);//或许队尾元素
size_t QueueSize(Queue* q);//或许队列长度
void QueuePush(Queue* q, QDataType x);//入队
void QueuePop(Queue* q);//出队
void QueuePrint(Queue* q);//打印队列元素
void QueueDestroy(Queue* q);//销毁队列
14.1.2. Queue.c
#include"Queue.h"
void QueueInit(Queue* q)
{q->front = NULL;q->rear = NULL;q->size = 0;
}
bool QueueEmpty(Queue* q)
{assert(q);return (q->front == NULL) && (q->rear == NULL);
}
QDataType QueueFront(Queue* q)
{assert(q);assert(!QueueEmpty(q));return q->front->data;
}
QDataType QueueBack(Queue* q)
{assert(q);assert(!QueueEmpty(q));return q->rear->data;
}
size_t QueueSize(Queue* q)
{return q->size;
}
void QueuePush(Queue* q, QDataType x)
{assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));newnode->data = x;newnode->next = NULL;if (newnode == NULL){perror("malloc fail");return;}if (q->front == NULL){q->front = q->rear = newnode;}else{q->rear->next = newnode;q->rear = newnode;}q->size++;
}
void QueuePop(Queue* q)
{assert(q);assert(!QueueEmpty(q));//1.只有一个结点if (q->front == q->rear){free(q->front);q->front = q->rear = NULL;}//2.有多个结点else{QNode* del = q->front;q->front = q->front->next;free(del);del = NULL;}q->size--;
}
void QueuePrint(Queue* q)
{assert(q);QNode* cur = q->front;QNode* tail = q->rear;printf("队头->");while (cur != tail->next){printf("%d->",cur->data);cur = cur->next;}printf("队尾\n");
}
void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->front;while (cur){QNode* del = cur;cur = cur->next;free(del);del = NULL;}q->front = q->rear = NULL;
}

14.2. 循环队列

14.2.1. Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDataType;
#define MAXSIZE 50  //定义元素的最大个数
/*循环队列的顺序存储结构*/
typedef struct {QDataType data[MAXSIZE];int front;  //头指针int rear;   //尾指针
}Queue;void QueueInit(Queue* q);//初始化队列
bool QueueEmpty(Queue* q);//判断是否为空
QDataType QueueFront(Queue* q);//获取队头元素
QDataType QueueBack(Queue* q);//或许队尾元素
size_t QueueSize(Queue* q);//或许队列长度
void QueuePush(Queue* q, QDataType x);//入队
void QueuePop(Queue* q);//出队
void QueuePrint(Queue* q);//打印队列元素
14.2.2. Queue.c
void QueueInit(Queue* q) 
{q->front = 0;q->rear = 0;
}
bool QueueEmpty(Queue*q)
{assert(q);return q->front == q->rear;
}
QDataType QueueFront(Queue* q)
{assert(q);assert(!QueueEmpty(q));return q->data[q->front];
}
QDataType QueueBack(Queue* q)
{assert(q);assert(!QueueEmpty(q));return q->data[q->rear-1];
}
size_t QueueSize(Queue*q) 
{assert(q);return (q->rear - q->front + MAXSIZE) % MAXSIZE;
}
void QueuePush(Queue* q, QDataType x) 
{assert(q);q->data[q->rear] = x;   q->rear = (q->rear + 1) % MAXSIZE;  //rear指针向后移一位置,若到最后则转到数组头部
}void QueuePop(Queue* q){assert(q);assert(!QueueEmpty(q));q->front = (q->front + 1) % MAXSIZE;    //front指针向后移一位置,若到最后则转到数组头部}void QueuePrint(Queue* q){assert(q);int cur = q->front;printf("队头->");while (cur != q->rear){printf("%d->", q->data[cur]);cur = (cur + 1) % MAXSIZE;}printf("队尾\n");}

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

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

相关文章

docker--部署 (超详版) (五)

环境准备&#xff1a;docker&#xff0c;mysql&#xff0c;redis&#xff0c;镜像&#xff0c;nginx 把虚拟机打开&#xff0c;连接xshell&#xff0c;参考博客&#xff1a; https://blog.csdn.net/m0_74229802/article/details/136965820?spm1001.2014.3001.5501 一&#x…

【微信加人自动化】RPA机器人:人人都会实现的机器人

用上这个机器人&#xff0c;一定要心平气和&#xff0c;不要放肆&#xff0c;单号忍住控制在15个人以内&#xff08;但悄悄的告诉你&#xff0c;可以切换账号呀&#xff09; 这个加人机器人&#xff0c;人人都可以通过学习自己动手实现&#xff0c;不再局限于遥不可及的“黑科…

千川素材投放效果如何追踪:精准识别爆款、潜力、首发、优质素材

在数字营销和广告领域&#xff0c;素材投放的效果直接关乎广告的成功与否。为了在竞争激烈的市场中脱颖而出&#xff0c;广告主和广告从业者需要密切关注素材投放效果&#xff0c;并及时识别出不同类型的素材&#xff0c;如爆款、潜力、首发和优质素材。本文将详细探讨如何进行…

2020年天津市二级分类土地利用数据(矢量)

天津市&#xff0c;位于华北平原海河五大支流汇流处&#xff0c;东临渤海&#xff0c;北依燕山。地势以平原和洼地为主&#xff0c;北部有低山丘陵&#xff0c;海拔由北向南逐渐下降&#xff0c;地貌总轮廓为西北高而东南低。天津有山地、丘陵和平原三种地形&#xff0c;平原约…

世界名校计算机类院系研究机器人的部分列举

计算机院系研究机器人方向的国外高校 一、美国高校 1.卡耐基梅隆大学 计算机学院官网 CMU School of Computer Science 注&#xff1a;CMU的机器人研究所在计算机学院下面&#xff0c;该学院还有其他系 Robotics Institute Carnegie Mellon University : Robotics Educati…

346CK01 噪声源,1 GHz 至 50 GHz

346CK01 噪声源 1 GHz 至 50 GHz Keysight 346CK01 是您使用是德科技噪声系数解决方案处理高频应用的理想伴侣。 凭借其宽带优势&#xff08;1 GHz 至 50 GHz&#xff09;&#xff0c;它可以顶替不同频段的多个噪声源。 另外&#xff0c;它的 SWR 也很低&#xff0c;消除了…

python函数参数中独立星号*的作用

python函数中间有一个&#xff08;&#xff09;分隔&#xff0c;星号后面为*命名关键字参数&#xff0c;星号本身不是参数**。命名关键字参数&#xff0c;在函数调用时必须带参数名字进行调用。如下例子&#xff1a;

mysql修改用户权限

https://blog.csdn.net/anzhen0429/article/details/78296814

【python】深入探讨flask是如何预防CSRF攻击的

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

哲学家带你深♂入了解文件操作

目录 一、文件指针 二、文件的打开与关闭 三、顺序读写函数的介绍 四、文件的随机读写 1、fseek 2、ftell 3、rewind 总结 前言 c语言中的文件操作虽然不怎么常用但也是非常重要的知识&#xff0c;今天由本哲学家带大家深♂入了解c语言文件操作。 一、文件指针 每个被使用的文…

基于FPGA实现的自适应三速以太网

一、三速以太网 千兆以太网PHY芯片是适配百兆和十兆的&#xff0c;十兆就不管了&#xff0c;我们的设计只适应千兆和百兆。 根据上图&#xff0c;我们是可以获取当前主机网口的速率信息的。 always(posedge w_rxc_bufr) beginif(w_rec_valid d0) beginro_speed < w_rec_…

【scala】使用gradle和scala构建springboot程序

零、版本说明: springboot: 2.7.18 使用log4j2&#xff0c;不使用springboot自带的logback scala版本&#xff1a;2.11 jackson版本&#xff1a;2.16.0 一、依赖&#xff1a; buildscript {dependencies {// using spring-boot-maven-plugin as package toolclasspath("…

北京WordPress建站公司

北京wordpress建站&#xff0c;就找北京wordpress建站公司 http://wordpress.zhanyes.com/beijing

【VSCode】解决远程配置jupyter notebook始终无法搜到kernel

问题 jupyter kernel一直无法选择&#xff0c;总是出现如下提示。反复点install/enable没有用处。 解决 首先确认Python Interpreter是否能正常选择。可能出现终端可以搜到conda env但vscode command palette中不显示的问题。如果不显示&#xff0c;尝试手动enable Python ex…

20232831 2023-2024-2 《网络攻防实践》第4次作业

目录 20232831 2023-2024-2 《网络攻防实践》第4次作业1.实验内容2.实验过程&#xff08;1&#xff09;ARP缓存欺骗攻击&#xff08;2&#xff09;ICMP重定向攻击&#xff08;3&#xff09;SYN Flood攻击&#xff08;4&#xff09;TCP RST攻击&#xff08;5&#xff09;TCP会话…

R语言使用dietaryindex包计算NHANES数据多种营养指数(2)

健康饮食指数 (HEI) 是评估一组食物是否符合美国人膳食指南 (DGA) 的指标。Dietindex包提供用户友好的简化方法&#xff0c;将饮食摄入数据标准化为基于指数的饮食模式&#xff0c;从而能够评估流行病学和临床研究中对这些模式的遵守情况&#xff0c;从而促进精准营养。 该软件…

C/C++中重载函数取地址的方法

目录 1.现象 2.指定参数取函数地址 3.利用Qt的类QOverload 1.现象 函数重载在C/C编码中是非常常见的&#xff0c;但是我们在std::bind或std::function绑定函数地址的时候&#xff0c;直接取地址&#xff0c;程序编译就会报错&#xff0c;示例如下&#xff1a; class CFunc1…

【C++】map set

文章目录 1. 关联式容器2. 键值对3. 树形结构的关联式容器3.1 set3.1.1 set 的介绍3.1.2 set 的使用 3.2 map3.2.1 map 的介绍3.2.2 map 的使用 3.3 multiset3.3.1 multuset 的介绍3.3.2 multiset 的使用 3.4 multimap3.4.1 multimap 的介绍3.4.2 multimap 的使用 1. 关联式容器…

Docker安装xxl-job并整合到SpringBoot项目

1. 创建数据库 执行如下SQL语句创建相关表 CREATE database if NOT EXISTS xxl_job default character set utf8mb4 collate utf8mb4_general_ci; use xxl_job;SET NAMES utf8mb4; CREATE TABLE xxl_job_info (id int(11) NOT NULL AUTO_INCREMENT,job_group int(11) NOT NUL…

PyCharm代码一键格式化

前言 每次写完代码&#xff0c;就看见很多警告&#xff0c;乍一看还以为我又写了什么bug&#xff0c;原来是提示&#xff0c;啊放心了。可是我可见不得这些东西&#xff0c;因为代码里都是红红的下划线&#xff0c;好像在圈圈点点。。。 代码格式化 于是利用PyCharm里的快捷…