当前位置: 首页 > article >正文

【数据结构与算法】数据结构基础——栈和队列

目录栈和队列1. 栈1.1 栈的概念1.2 栈的实现方式分析1.3 栈的实现1.3.1 栈的初始化与销毁1.3.2 入栈与出栈1.3.3 栈的判空与有效元素个数1.3.4 栈顶元素1.4 栈的扩展1.4.1 两栈共享空间2. 队列2.1 队列的概念2.2 队列的实现方式分析2.3 队列的实现2.3.1 队列的初始化与销毁2.3.2 入队与出队2.3.3 队列的判空与队头队尾2.3.4 队列的有效元素个数2.4 循环队列栈和队列1. 栈1.1 栈的概念栈是一个特殊的线性表。栈只能在一端进行插入元素和删除元素的操作其中能进行操作的一端称为栈顶另一端称作栈底。其具有先进后出LIFO(last in first out)的性质栈的示意图 栈的示意图栈的示意图特殊术语入栈压栈在栈顶插入元素称为入栈出栈在栈顶删除元素称为出栈1.2 栈的实现方式分析【分析】我们根据栈的特殊性质需要选一个更适合的线性结构来实现它对比维度顺序表链表访问元素随机访问时间复杂度 O(1)遍历链表时间复杂度 O(n)插入/删除头部需要移动后面的所有元素O(n)只需修改指针O(1)插入/删除尾部若空间足够O(1)需要遍历到尾部O(n)单链表插入/删除中间需要移动元素O(n)找到位置后修改指针O(1)应用场景需要频繁随机访问、尾部操作密集、数据量相对固定需要频繁在任意位置插入删除、数据量变化大经过顺序表和链表的对比我们发现因为栈只能在栈顶尾部进行插入和删除操作顺序表在尾部的操作时间复杂度是O(1)而链表为O(n)。所以我们更适合使用顺序表来实现栈——【动态顺序表详解】——1.3 栈的实现实现方式顺序表实现动态分配内存typedefintSTDataType;typedefstructStack{STDataType*data;intsize;//顺序表有效元素个数intcapacity;//顺序表的容量}Stack;这里与动态顺序表的定义方式一样就不过多赘述了……1.3.1 栈的初始化与销毁栈的初始化时间复杂度O(1)voidStackInit(Stack*st){assert(st);st-dataNULL;st-sizest-capacity0;};这里关于assert(断言)防止空指针的解引用是一个好习惯可以防止很多莫名其妙的BUG栈的销毁时间复杂度O(1)voidStackDestroy(Stack*st){free(st-data);st-dataNULL;st-sizest-capacity0;}1.3.2 入栈与出栈入栈时间复杂度O(1)voidStackPush(Stack*st,STDataType x){assert(st);//判断容量是否足够 不够就扩容if(st-sizest-capacity){intnewcapacityst-capacity0?4:st-capacity*2;STDataType*tmp(STDataType*)realloc(st-data,newcapacity*sizeof(STDataType));if(tmpNULL)exit(-1);st-datatmp;st-capacitynewcapacity;}//插入元素st-data[st-size]x;st-size;}出栈时间复杂度O(1)voidStackPop(Stack*st){assert(st);assert(!empty(st));//判断栈非空st-size--;}1.3.3 栈的判空与有效元素个数判空时间复杂度O(1)boolempty(Stack*st){assert(st);returnst-size0;}有效元素个数时间复杂度O(1)intsize(Stack*st){returnst-size;}1.3.4 栈顶元素获取栈顶元素时间复杂度O(1)STDataTypetop(Stack*st){returnst-data[st-size-1];//从下标0开始存储元素}小结以上就是栈的基本的实现操作起来并不难不过再提醒一句因为实现数据结构用了大量指针所以在操作时一定要给指针判空和将没有用的指针及时置为NULL1.4 栈的扩展1.4.1 两栈共享空间根据栈的特性我们使用顺序表来实现栈。可是当栈的存储空间满了的时候我们需要去为这个栈扩容每次一满就要扩容这会有很多时间上的消耗。如果此时有两个相同类型的栈一个栈的存储空间快溢出了另外一个确还是有很多空闲的空间那我们何不根据栈的特性让两个栈合并使得空间的使用率更高两个栈合并实际上就是让两个栈共同使用同一个数组顺序表栈1的栈顶指针top1从-1开始栈2的栈顶指针top2从capacity数组的最大容量开始如下图当栈1需要入栈的时候top1指针就像右移栈2需要入栈时top2向左移那么要如何判断栈是否满了呢我们先看两个特殊情况【1】top1直接走到了最右边即两栈共享的空间全部都是栈1的元素此时情况如下此时如果栈满了就会满足top2 - top1 1【2】与第一种情况相反top2直接走到了最左边同理栈满时有top2 - top1 1。此时还有一种一般情况如下也是当top2 - top1 1时栈满了。所以得出结论当top2 - top1 1时栈就满了应用场景两栈共享空间一般在两个栈满足此消彼长的条件时使用即栈1元素增加时栈2的元素就要减少就像买股票当你买入了一份股票之后那一定有人持有的股票减少了相反的如果两个栈都是一直在插入元素的话空间很快就会满而设计这样的结构也就没意义了。两栈共享空间只是一个技巧适合两个栈存储的是相同的数据类型如果是不同的数据类型使用这种存储方式只会使操作更复杂2. 队列2.1 队列的概念队列是一种只能在一端插入数据另一端删除数据FIFO(first in first out)的特殊的线性表。其中插入数据的一端称为队尾删除数据的一端称作队头。队列结构示意图 队列结构示意图队列结构示意图关于队列的特殊术语队头队列出队一端的第一个元素队尾队列入队一端的第一个元素入队在队尾插入元素称为入队出队在队头删除元素称为出队2.2 队列的实现方式分析【分析】根据队列的特殊性质找一种最适合的数据结构来实现它这里再次拿到上面的对比表格对比维度顺序表链表访问元素随机访问时间复杂度 O(1)遍历链表时间复杂度 O(n)插入/删除头部需要移动后面的所有元素O(n)只需修改指针O(1)插入/删除尾部若空间足够O(1)需要遍历到尾部O(n)单链表插入/删除中间需要移动元素O(n)找到位置后修改指针O(1)应用场景需要频繁随机访问、尾部操作密集、数据量相对固定需要频繁在任意位置插入删除、数据量变化大队列与栈不同栈只能在栈顶的一端进行插入删除等操作。而队列是在队头和队尾两端都进行频繁的操作所以考虑两种线性表两端的操作经过对比发现顺序表和链表在头部操作和尾部操作都各自有优势所以我们此时就要考虑使用哪个可以优化或者更方便优化【优化】考虑到链表在尾部的操作是O(n)的原因是每次都需要遍历一遍链表才可以找到尾节点那何不干脆就直接将尾节点保存下来。将链表的尾节点保存下来之后就可以将链表尾部的操作优化到O(1)了【结论】使用链表来实现队列2.3 队列的实现队列的结构typedefintQDataType;//链表节点的结构typedefstructQueueNode{QDataType data;structQueueNode*next;}QueueNode;//队列的结构typedefstructQueue{QueueNode*phead;//指向队列的头节点QueueNode*ptail;//指向队列的尾节点}Queue;2.3.1 队列的初始化与销毁队列的初始化voidQueueInit(Queue*pq){assert(pq);pq-pheadp-ptailNULL;}队列的销毁voidQueueDestory(Queue*pq){assert(pq);QueueNode*pcurpq-phead;while(pcur){QueueNode*pnextpcur-next;free(pcur)pcurpnext;}//最后将头指针和尾指针置为NULL 防止野指针pq-pheadpq-ptailNULL;}【注意】销毁队列后要记得将phead和ptail置为NULL防止野指针2.3.2 入队与出队入队voidpush(Queue*pq,QDataType x){assert(pq);//动态申请节点QueueNode*newNode(QueueNode*)malloc(sizeof(QueueNode));if(newNodeNULL){perror(malloc fail);exit(-1);}newNode-datax;newNode-nextNULL;//如果队列不为空if(pq-phead){pq-ptail-nextnewNode;pq-ptailnewNode;}else{//队列为空pq-pheadpq-ptailnewNode;}}出队voidpop(Queue*pq){assert(pq);assert(!empty(pq));//当队列只有一个节点时if(pq-pheadpq-ptail){free(pq-phead);pq-pheadpq-ptailNULL;}else{QueueNode*pnextpq-phead-next;free(pq-phead);pq-pheadpnext;}}这里要注意出队是要分两种情况【1】当队列只有一个节点时【2】当队列有多个节点时2.3.3 队列的判空与队头队尾//判空boolempty(Queue*pq){returnpq-pheadNULL;}//返回队头元素QDataTypeQueueFront(Queue*pq){assert(pq);assert(!empty(pq));returnpq-phead-data;}//返回队尾元素QDataTypeQueueback(Queue*pq){assert(pq);assert(!empty(pq));returnpq-ptail-data;}2.3.4 队列的有效元素个数intsize(Queue*pq){assert(pq);QueueNode*pcurphead;intsize0;while(pcur){size;pcurpcur-next;}returnsize;}读到这里可能就会有读者有疑惑前面所有的操作时间复杂度都是O(1)怎么到这里时间复杂度就变成O(n)了或许有的人会觉得这就是链表的缺陷是不可更改的但是优化的方法其实很简单。既然前面队列的结构都已经维护头指针和尾指针那干脆就再维护一个队列的长度但是维护一个size就意味着代码会更复杂所以也需要分情况来定义和维护【1】如果在不需要频繁的获取队列的长度的情况下就继续使用之前的方法【2】如过需要频繁获取队列的长度就再维护一个队列长度sizetypedefstructQueue{QueueNode*phead;//指向队列的头节点QueueNode*ptail;//指向队列的尾节点intsize;}Queue;维护 s i z e 的版本 维护size的版本维护size的版本2.4 循环队列引入上文说到实现队列这个数据结构时使用链表实现会更好。原因在于使用顺序表实现队列的优化没有链表好但是使用顺序表来实现队列其实也是有它自己的优化方式的顺序表示实现普通队列如下是一个使用顺序表实现队列的结构初始时定义队头指针front和队尾指针rear指向顺序表的开头上文中提到使用顺序表实现队列最大的问题就在于顺序表头部操作的时间复杂度为O(n)也就是出队需要花费很多时间。但是其实是因为顺序表每次进行头部操作时要移动后面的元素才使得时间效率低所以就在这里对头部操作的优化对于每次出队只需将front指针向后移动即可顺序表实现循环队列到这里用顺序表实现队列在时间上的问题解决了但是此时又发现当rear指针走到顺序表的最后时队列就算满了而队列的前面还有很多的空闲的位置没有使用这样就导致了很多的空间浪费此时就应该想一个方法使得顺序表前面的空间也可以被使用。将队列的整体看成是一个环当rear或front要越界的时候再让它们跳回到顺序表的起始位置就跟一个环一样实现方式每当入队完成时(push)rear (rear 1) % size每当出队完成时(pop)front (front 1) % size【注】size是顺序表的长度循环队列初始状态 循环队列初始状态循环队列初始状态由上述可知当队列为空时front rear再看当队列满了的时候此时的判断条件还是front rear这样的话当front指针等于rear指针时根本就不知道队列此时是空的还是满的所以还需要修改。在顺序表中一直保留一个位置不使用此时判断队列为满的条件就变成了front - rear 1特别的如果需要队列的有效元素的个数的话int length (rear - front size) % size循环队列的代码实现typedefstruct{int*data;intcapacity;intfront;intrear;}MyCircularQueue;//初始化MyCircularQueue*myCircularQueueCreate(intk){//创建循环队列MyCircularQueue*cq(MyCircularQueue*)malloc(sizeof(MyCircularQueue));cq-dataNULL;cq-frontcq-rear0;//分配内存int*tmp(int*)malloc((k1)*sizeof(int));if(!tmp)exit(-1);cq-datatmp;cq-capacityk1;returncq;}//入队 如果入队成功返回true反之返回falseboolmyCircularQueueEnQueue(MyCircularQueue*obj,intvalue){assert(obj);intsizeobj-capacity;//如果队列满了 就返回falseif((obj-rear1)%sizeobj-front){returnfalse;}else{obj-data[obj-rear]value;obj-rear(obj-rear1)%size;//更新rear的值returntrue;}}//出队 如果出队成功返回true反之返回falseboolmyCircularQueueDeQueue(MyCircularQueue*obj){assert(obj);intsizeobj-capacity;if(obj-rearobj-front){returnfalse;}else{obj-front(obj-front1)%size;returntrue;}}//返回队列头部元素intmyCircularQueueFront(MyCircularQueue*obj){assert(obj);if(obj-rearobj-front){return-1;}else{returnobj-data[obj-front];}}//返回队列队尾元素intmyCircularQueueRear(MyCircularQueue*obj){assert(obj);intsizeobj-capacity;if(obj-rearobj-front){return-1;}else{returnobj-data[(obj-rear-1size)%size];}}//判断是否为空队列boolmyCircularQueueIsEmpty(MyCircularQueue*obj){assert(obj);returnobj-frontobj-rear;}//判断队列是否满了boolmyCircularQueueIsFull(MyCircularQueue*obj){assert(obj);intsizeobj-capacity;return(obj-rear1)%sizeobj-front;}//销毁队列voidmyCircularQueueFree(MyCircularQueue*obj){assert(obj);free(obj-data);obj-dataNULL;obj-rearobj-frontobj-capacity0;free(obj);}

相关文章:

【数据结构与算法】数据结构基础——栈和队列

目录栈和队列1. 栈1.1 栈的概念1.2 栈的实现方式分析1.3 栈的实现1.3.1 栈的初始化与销毁1.3.2 入栈与出栈1.3.3 栈的判空与有效元素个数1.3.4 栈顶元素1.4 栈的扩展1.4.1 两栈共享空间2. 队列2.1 队列的概念2.2 队列的实现方式分析2.3 队列的实现2.3.1 队列的初始化与销毁2.3.…...

Matlab,plot绘图如何添加边框

matlab生成的图——编辑(E)——坐标区属性(A)——框样式——Box,勾选效果:...

HarmonyOS 6学习:解决图片放大后无法移动至边缘的matrix4矩阵变换技巧

从"卡在中间"到"自由拖拽":一次完整的图片缩放平移边界问题攻关在HarmonyOS 6应用开发中,我最近遇到了一个看似简单却让人头疼的图片查看器问题:用户双指放大图片后,想要拖动查看边缘细节,却发现图…...

二十六.签名与脚本(1)--脚本介绍

1.区块链脚本介绍在之前的章节中,我们了解了签名与验证相关,但是btc的交易数据,签名和验证,不是单纯的,还有脚本深度参与其中。我们从开始来:bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletT…...

高精度光照检测

光线检测仪,kotlin开发,调用手机感光模块检测室内外光照强度,用途多多,我主要用途孩子写作业检测光照保护视力。 食用方法∶打开即测,速度快,无广告,手机平视即可,无须直视光线。 买…...

独立开发者如何利用Taotoken Token Plan,以更低成本启动AI项目

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 独立开发者如何利用Taotoken Token Plan,以更低成本启动AI项目 对于独立开发者或小型团队而言,启动一个集成…...

Taotoken的审计日志功能为企业API安全与合规管理提供支持

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Taotoken的审计日志功能为企业API安全与合规管理提供支持 当企业决定将大模型能力集成到内部业务流程中时,IT管理员和安…...

为你的Hermes Agent自定义Provider,接入Taotoken多模型池

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 为你的Hermes Agent自定义Provider,接入Taotoken多模型池 在构建复杂的AI应用时,开发者常常面临一个核心挑…...

艾尔登法环存档迁移终极指南:3分钟解决角色转移难题

艾尔登法环存档迁移终极指南:3分钟解决角色转移难题 【免费下载链接】EldenRingSaveCopier 项目地址: https://gitcode.com/gh_mirrors/el/EldenRingSaveCopier 还在为《艾尔登法环》存档版本不兼容而烦恼吗?EldenRingSaveCopier 是你的终极解决…...

3分钟开启PC游戏分屏派对:NucleusCoop让单机游戏秒变多人同屏神器

3分钟开启PC游戏分屏派对:NucleusCoop让单机游戏秒变多人同屏神器 【免费下载链接】nucleuscoop Starts multiple instances of a game for split-screen multiplayer gaming! 项目地址: https://gitcode.com/gh_mirrors/nu/nucleuscoop 还在为热门PC游戏不支…...

GIS工程应用记录(AI辅助编程)

问题的问题:语境坍缩“从各个角度提出问题,AI做出对应积极答复和修改,结果没有什么变化。”这,就是元问题最核心的症状。你尝试了所有你已知的“高级”协作手段,但就像重拳打在棉花上,AI永远在积极回应&…...

脉冲神经网络加速器设计与边缘计算优化

1. 脉冲神经网络加速器的设计挑战与突破在边缘计算领域,脉冲神经网络(SNN)正以其独特的生物启发特性引发新一轮技术变革。与传统人工神经网络(ANN)相比,SNN通过离散的脉冲信号传递信息,模拟生物神经元的工作机制,理论上可实现超低…...

OpenIPC开源固件:5分钟解锁网络摄像头的终极控制权

OpenIPC开源固件:5分钟解锁网络摄像头的终极控制权 【免费下载链接】firmware Alternative IP Camera firmware from an open community 项目地址: https://gitcode.com/gh_mirrors/fir/firmware 还在为网络摄像头的封闭系统而烦恼吗?想要完全掌控…...

DS4Windows终极指南:3步让PS手柄在PC上完美运行游戏

DS4Windows终极指南:3步让PS手柄在PC上完美运行游戏 【免费下载链接】DS4Windows Like those other ds4tools, but sexier 项目地址: https://gitcode.com/gh_mirrors/ds/DS4Windows 还在为PS手柄连接Windows电脑后无法识别而烦恼吗?&#x1f3ae…...

如何在3分钟内为任何活动搭建专业级滚动抽奖系统?Magpie-LuckyDraw全平台开源方案深度解析

如何在3分钟内为任何活动搭建专业级滚动抽奖系统?Magpie-LuckyDraw全平台开源方案深度解析 【免费下载链接】Magpie-LuckyDraw 🏅A fancy lucky-draw tool supporting multiple platforms💻(Mac/Linux/Windows/Web/Docker) 项目地址: https…...

Ubuntu经常安装软件

1、垃圾清理工具stacer sudo apt updatesudo apt install stacer apt cleanapt autocleanapt autoremove 2、类似与everything的工具Fsearcch 1sudo add-apt-repository ppa:christian-boxdoerfer/fsearch-stable 2sudo apt update 3sudo apt install fsearch (注&#xf…...

ZMJS,把 JavaScript 解释器放进 SAP ABAP 应用服务器之后,很多扩展思路会变得不一样

我今天看这个 oisee/zmjs 仓库时,最吸引人的不是它把 JavaScript 语法做进了 ABAP,而是它选择了一条非常 SAP 的路线,纯 ABAP、无外部依赖、无 Kernel Module、以类和接口的形式运行在 SAP 应用服务器内部。仓库自己的定位很直接,ZMJS 是一个面向 SAP ABAP 的 Mini JavaScr…...

航空发动机叶片三维扫描-诺斯顿

航空发动机叶片作为发动机的核心动力部件,其精度与性能直接决定发动机的推力、燃油效率及运行安全性,三维扫描技术作为航空制造领域的核心数字化手段,已广泛应用于叶片全生命周期的多个关键环节。其应用涵盖叶片研发设计阶段的逆向工程&#…...

LaTeX公式一键转Word:3步告别数学公式编辑烦恼

LaTeX公式一键转Word:3步告别数学公式编辑烦恼 【免费下载链接】LaTeX2Word-Equation Copy LaTeX Equations as Word Equations, a Chrome Extension 项目地址: https://gitcode.com/gh_mirrors/la/LaTeX2Word-Equation 还在为Word文档中的数学公式编辑而抓狂…...

打造XBEE封装BLE112蓝牙模块:硬件设计、射频布局与调试全攻略

1. 项目概述:为什么我们需要一个“XBEE格式”的蓝牙模块?在嵌入式开发和物联网项目中,无线通信模块的选择往往决定了项目的成败。对于很多工程师和创客来说,Silicon Labs(芯科科技)的BLE112/113模块是蓝牙4…...

Codex使用API Key授权无法使用插件?

小伙伴们,大家好,我是小溪,见字如面。对于没有ChatGPT账号的小伙伴来说,虽然可以通过API Key授权的方式使用Codex桌面端,但是会有一些限制。比如无法使用插件功能,无法使用Codex移动端进行远程控制等。为了…...

LVGL多页面开发避坑:用内部Timer替代轮询,解决页面切换时的内存踩踏问题

LVGL多页面开发中的内存安全实践:用Timer机制替代轮询的工程解决方案 在嵌入式UI开发中,LVGL因其轻量级和跨平台特性成为热门选择。但当项目复杂度提升到多页面交互时,开发者往往会遇到一个棘手问题:如何在频繁切换页面的同时保证…...

1688运营培训/询盘成本从500元降到63.9!1688运营培训还原1688真实玩法

1688运营培训/询盘成本从500元降到63.9!1688运营培训还原1688真实玩法500块钱一个询盘,你敢信?做1688运营培训这么多年,这个数字我都觉得离谱。前阵子遇到一个老板,一上来就开始吐槽1688,说1688就是个垃圾平…...

告别Postman!用APIfox搞定接口测试+自动化,这份保姆级教程带你从环境配置到报告生成

从Postman到APIfox:接口测试自动化的高效迁移指南如果你还在为接口测试中的重复劳动和多环境切换头疼,是时候考虑从Postman迁移到APIfox了。作为一名经历过这个转型过程的开发者,我想分享一些实战经验,帮助你平滑过渡并最大化利用…...

用Azure Kinect DK和Body Tracking SDK,5分钟实现一个实时人体骨骼点检测Demo(C++版)

5分钟实战:用Azure Kinect DK实现实时人体骨骼点追踪(C版) 当你第一次拿到Azure Kinect DK时,最令人兴奋的莫过于它强大的人体追踪能力。这款深度相机不仅能捕捉高清彩色图像,更能通过AI算法实时重建人体骨骼关节点。本…...

【python】ImportError: DLL load failed while importing QtWidgets: 找不到指定的程序。重新安装后搞定

文章目录前言一、PyQt6引用后报错二、使用步骤总结前言 想做个好看的界面,引用了PyQt6,却产生了新问题。 pip install pyqt6-tools,优先做这个动作进行修复。 一、PyQt6引用后报错 python里引用: from PyQt6.QtWidgets import…...

榨干Codex!OpenAI工程师亲授Codex真正用法

你可能把 Codex 当编程助手用,改改代码,跑跑测试。但它的能力远不止于此。OpenAI 的客户支持工程师 Jason(jxnlco)告诉你,Codex 其实是一套完整的电脑工作系统,从语音输入到自动化,从浏览器操控…...

真可用!美团数字人模型开源,MV、电商等统统拿下

美团开源的数字人视频生成框架 LongCat-Video-Avatar 刚刚更新到 1.5 版本。是真能用。这版更新把音频编码器换了,推理步数砍到8步,在770人、13240条主观评分的大规模评测里,雷达图面积全面领先。音频编码器换血,8步出图LongCat-V…...

yolo视频识别 车辆速度估计识别 yolo11视频实时速度测量与测速估计

文章目录YOLOv11:视频实时速度测量与测速估计一、YOLOv11概述二、速度测量原理三、距离测量方法四、应用场景五、实践案例以下是关于使用YOLOv11进行视频实时速度测量与测速估计的介绍: YOLOv11:视频实时速度测量与测速估计 随着计算机视觉…...

十年以上经验的建站公司推荐|策划强、落地稳的网站制作公司盘点

互联网时代,企业官网已从单纯的信息展示窗口升级为集品牌价值传递、用户体验连接与业务高效转化于一体的核心数字阵地。行业报告显示,优质官网可帮助企业线上转化率提升35%-60%,而低效官网则可能导致潜在客户大量流失。面对市场上众多的网站建…...