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

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

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

所属专栏:数据结构与算法
贝蒂的主页: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;不再局限于遥不可及的“黑科…

通俗易懂:volatile关键字在内存模型中起到什么作用?

在计算机编程语言中&#xff0c;尤其是在Java语言中&#xff0c;volatile关键字对于内存模型的影响至关重要&#xff0c;它主要用于解决多线程环境下的内存可见性和禁止指令重排序问题&#xff0c;以确保并发编程的正确性。以下是volatile关键字在Java内存模型&#xff08;JMM&…

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

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

Vue实现SQL语句关键字高亮显示?

SQL关键字高亮 要在Vue中实现SQL语句中关键字的高亮显示&#xff0c;你可以使用类似的方法&#xff0c;但是你需要根据SQL语法的特点来解析并高亮显示关键字。以下是一个示例代码&#xff0c;演示了如何在Vue中实现SQL语句关键字的高亮显示。 <template><div><…

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;

第十六章 Redies

一、Redies Remote Dictionary Service 内存存储&#xff0c;NoSQL。基于内存来存储数据。无需 IO&#xff0c;效率高。提供高可用方案。 哨兵模式&#xff0c;分布式数据存储。 1.1 Redis 基本特性 - 关系型数据与非关系型数据库对比。 - SQL > 1. 行存储&#xff0c;…

探索图数据处理的魅力:使用Spark GraphX解析图数据和应用图算法

导语&#xff1a;在当今数据驱动的世界中&#xff0c;图数据处理和分析变得越来越重要。本文将介绍如何使用Spark GraphX&#xff0c;一个强大的图计算库&#xff0c;来处理和分析图数据。通过详细的Java代码示例和模拟输出结果&#xff0c;你将了解如何创建图、执行图操作和应…

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_…

虚拟DOM和真实DOM的区别

虚拟DOM&#xff08;Virtual DOM&#xff09;、DOM&#xff08;Real DOM&#xff09;是前端开发中常用的两种概念。 什么是真实DOM&#xff1f; ​ 真实DOM是浏览器中实际存在的DOM结构&#xff0c;它由浏览器解析HTML生成&#xff0c;并且直接与浏览器交互。当页面中的数据发…

【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("…

什么是数据湖

什么是数据湖 数据湖是目前比较热的一个概念,许多企业都在构建或者计划构建自己的数据湖。但是在计划构建数据湖之前,搞清楚什么是数据湖,明确一个数据湖项目的基本组成,进而设计数据湖的基本架构,对于数据湖的构建至关重要。关于什么是数据湖?有不同的定义。 Wikipedia…

北京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…