与队列和栈相关的【OJ题】

32becbd905a5411d8b4827566ec69ec4.jpeg

✨✨✨专栏:数据结构     

          🧑‍🎓个人主页:SWsunlight

5d474da1272640588145b2285d51bdf6.gif

目录

 

 一、用队列实现栈:

1、2个队列的关联起来怎么由先进先出转变为先进后出:(核心)

 2、认识各个函数干嘛用的:

3、代码实现:

二、用栈实现队列:

1、题目:

2、思路:

 3、代码:

 三、设计循环队列:

1、题目:

 2、思路:

​编辑

3、代码:


 

 一、用队列实现栈:

内容如下:

654d26f0b2d54dc5863caf4f8aeb162f.png

 2个队列实现栈::首先考虑的是  栈的规则:先进后出(后进先出)

                                                   队列的规则:先进先出

1、2个队列的关联起来怎么由先进先出转变为先进后出:(核心)

e59c31563ecf4ca6834c8ea8acaba4c1.png

 如上图,外围框架(虚构的,为了更好理解,让他具体化)就是我要实现的栈,而我要通过2个队列来实现栈,是不是是可以让小球先走上面的“通道”进去,全部小球(元素)进入上“通道”中,此时我排在通道最后的小球(元素)就是我要出栈的第一个数据。


如下图:2号球要第一个出栈,我们发现下“通道”是空的,那么我让2号球前面的球离开这个“通道”去到下面的通道,上通道就会剩下一个2号球,此时取2号球顺利的第一个走出去,就相当于栈的Top(将后进的元素取出)

547bcd2e0e4648da913d69dc63216c8b.png

1125b438160c4a6c9c9f4f606d909ecf.png 反复进行,就可以实现栈了,(队尾的元素能出栈,其他位置得绕着2个通道来回转(有点像明知她(他)不爱你,你还是要困死再这颗树),只有自己在成为队尾了(心灰意冷了),才会幡然醒悟(不能再一直停留再原地绕圈了,要向前看啦)

 2、认识各个函数干嘛用的:

可能只是对我而言 

我已经标好了,各个函数的功能:知道功能就好实现了

0d3dd4f7532b4948a150a2dc1a87a1ef.png

这个结构体的成员放2个队列即可:

 cae2ea53b23f43639dfcbb60bf923c8d.png

1c407e0554a442beab0e45d4de98d083.png

 结构体MyStack嵌套2个(队列)结构体(Queue)——>Queue的结构体在嵌套节点的结构体27709a53582f41e286b913b818744c67.png

如下:头节点后面还会链接更多的尾节点

1b07bf0d9a4940508dd3fa335af91bf9.png

 

3、代码实现:

将之前写队列的代码复制过来直接可以用了

typedef int QUDataType;
//节点
typedef struct QueueNode{QUDataType a;//一定要用指针,不然结构体的大小就无法确定了struct QueueNode *next;
}QuNode;
//再建立一个保存头和尾的结构体
typedef struct Queue {QuNode* head;QuNode* tail;int size;
}Queue;//初始化头尾节点
void QuInto(Queue* q)
{//不能传空指针  即(q=NULL)assert(q);q->head = NULL;q->tail = NULL;q->size = 0;
}//尾插(2种情况:1.头尾都为NUL  2.又数据入队列了)
void QuPush(Queue* q, QUDataType x)
{//申请空间QuNode* newnode = (QuNode*)malloc(sizeof(QuNode));//判空if (newnode == NULL){perror("malloc");return;}newnode->a = x;newnode->next = NULL;//节点创建完成//判断尾的位置if (q->tail == NULL){q->head = q->tail = newnode;}else{q->tail->next = newnode;q->tail = newnode;}q->size++;
}//头删(删除到尾以后,就不能再删了)
void QuPop(Queue* q)
{assert(q);//头的位置也不能为空assert(q->size!=0);//此时数据个数为1(也就是最后一个节点)if (q->head->next==NULL){free(q->head);q->head = q->tail = NULL;}else//q->head != q->tail;{QuNode* next = q->head->next;free(q->head);q->head = next;}//数据个数也要减去q->size--;}//判空
bool QuEmpty(Queue* q)
{assert(q);//头尾都相等时到达同一个位置,此时就为真,其他的情况都为假;return q->size==0;
}//取头数据
QUDataType QuFront(Queue* q)
{assert(q);assert(q->head);return q->head->a;
}//取尾数据
QUDataType QuBack(Queue* q)
{assert(q);//队尾都为空了,已经没数据了assert(q->tail);return q->tail->a;
}
//数据个数
int QuSize(Queue* q)
{assert(q);return q->size;
}//销毁空间(写进数据,想要一次性释放完就来用)
void QuDestroy(Queue* q)
{assert(q);QuNode* cur = q->head;while (cur){QuNode* next = cur->next;free(cur);cur = next;}q->head = q->tail =NULL;q->size = 0;
}//2个队列
typedef struct {Queue q1;Queue q2;
} MyStack;//初始化
MyStack* myStackCreate() {MyStack*pts = (MyStack*)malloc(sizeof(MyStack));if(pts==NULL){perror("malloc");//exit(1);return NULL;}QuInto(&pts->q1);QuInto(&pts->q2);return pts;  
}
//插入
void myStackPush(MyStack* obj, int x) {//判空插入,将数据插入不为空的队列if(!QuEmpty(&obj->q1)){QuPush(&obj->q2,x);}else{QuPush(&obj->q2,x);}
}
//删除并取出top元素
int myStackPop(MyStack* obj) {//假设法:no存不为空Queue* noEmpty = &obj->q1;Queue* empty = &obj->q2;if(!QuEmpty(empty)){noEmpty = &obj->q2;empty = &obj->q1;}//我们要让size-1个数据去到那条空通道while(QuSize(noEmpty)>1){//头删,所以取头int x = QuFront(noEmpty);QuPop(noEmpty);//将其数据存到size-1个数据存入空道QuPush(empty,x);}//随便头还是尾取,因为此时这通道只有最后一个元素了int top =QuFront(noEmpty);QuPop(noEmpty);return top;
}
//直接取top元素
int myStackTop(MyStack* obj) {//不要删除,只是取元素,那么取不为空的通道的队尾元素(因为根据栈的原则:先出的是队尾元素)if(!QuEmpty(&obj->q1)){return QuBack(&obj->q1);}else{return QuBack(&obj->q2);}
}
//判空
bool myStackEmpty(MyStack* obj) {//2个都是空同通道则为真,否则为假return QuEmpty(&obj->q1)&&QuEmpty(&obj->q2);
}
//销毁
void myStackFree(MyStack* obj) {QuDestroy(&obj->q1);QuDestroy(&obj->q2);free(obj);obj = NULL;
}

 关于销毁:要先从小(从内向外)的开始,我们应该先从q1和q2进行销毁,在销毁obj

obj申请的空间是为2个队列开辟的,2个队列申请的空间又是为里面的单链表开辟的,你直接销毁大哥,小弟起步就是群龙无首,变成了“野狗”

二、用栈实现队列:

1、题目:

223cd369447d0410cad2cce24a6a26447.png

2、思路:

和上面思路大差不差

先画图:和上面说的类似,不做赘述

b69501cbeb4f4a33a6f8b44b5001e528.png

 区别1:栈的top指向的是  栈顶元素   还是  栈顶元素下一个位置

根据之前我写的top指向的是栈顶元素的下一个位置叙说:如下

没有数据是top = 0;当存入一个时top = 1;

4748e8bc1d484d5f982b4e29d9351807.png

 af765a10d7494fcb9dad3d46826fb0f1.png

 所以你再取数据空的栈时应该也要注意先pop一下再取

 还有一个需要注意的点:它比队列实现栈跟倔强(醒悟的更慢)!!

一个栈里面的数据如下:

04cff4a000bd4378b877afd5f1568bca.png

 4 3 2 取出放到下面的空栈中

 流程图如下:1 就顺利走了,以为这样就完事了??一开始我也这样想的,结果碰壁了

e627c9bb9bbb4ae19b040ef66478db1f.png

将1取走后,根据之前的思路,再pop时就会将3 2放到上面栈,那么最后出队顺序变成了:1 4 2 3

乱套了

非常不对劲

 我想到了再回转一次,这样就可以保证我开始入队时的顺序不变,也就是将上面的栈作为出数据用,每次出一次数据,就将其他数据入栈到下面的栈,出完再回到上面的栈,无论你423519e7ecb74c08b99aa6daccc1ccb5.png

可以理解为:不撞南墙不回头(心死了,才向前)!!!!头比较硬哈,越靠后撞的次数越多

9426bce6cbeb4acf8c687ee868f426b1.png

 3、代码:

有更好的代码,作参考即可

typedef int LTDataType;
//顺序表(栈)
typedef struct SL
{LTDataType* a;int top;int capacity;
}SL;
//入栈
void SLPush(SL* p,LTDataType x)
{//不能传NULL,判空;assert(p);if (p->top == p->capacity){//先判断是否为0,好进行扩容int newnode = p->capacity == 0 ? 4 : 2 * (p->capacity);//扩容;创建一个临时变量接收新的空间,成功在将其交给p->a;LTDataType* s = (LTDataType*)realloc(p->a,newnode * sizeof(LTDataType));if (s == NULL){perror("realloc");return;}p->a = s;p->capacity = newnode;}p->a[p->top] = x;//指向下一个数据地址p->top++;
}
//出栈(类似尾删)
void SLPop(SL* p)
{//是否为空assert(p);assert(p->top > 0);p->top--;
}
//初始化
void SLInit(SL* p)
{p->a = NULL;p->capacity = 0;//p->top = -1;//指向栈顶的数据p->top = 0;//指向栈顶的下一个数据
}
//销毁
void SLDestroy(SL* p)
{assert(p);free(p->a);p->a = NULL;p->capacity = p->top = 0;
}
//判空
bool SLEmpty(SL* p)
{//不能是空地址assert(p);//为0就是真(true),为1就是假(flase)return p->top == 0;
}
//数据个数
int SLsize(SL* p)
{int size = p->top;return size;
}
//取数据:
LTDataType SLPot(SL*p)
{assert(p);return p->a[p->top];
}//2个栈
typedef struct {SL q1;SL q2;
} MyQueue;//初始化
MyQueue* myQueueCreate() {MyQueue*pts = (MyQueue*)malloc(sizeof(MyQueue));if(pts==NULL){perror("malloc");return NULL;}SLInit(&pts->q1);SLInit(&pts->q2);return pts;
}
//入队
void myQueuePush(MyQueue* obj, int x) {//非空,存数据:一定要注意top,我的top是指向栈顶元素的下一个位置if(!SLEmpty(&obj->q1)){SLPush(&obj->q1,x);}else{SLPush(&obj->q2,x);}
}
//出队
int myQueuePop(MyQueue* obj) {//假设法:SL *noEmpty =&obj->q1;SL *empty =&obj->q2;if(!SLEmpty(empty)){noEmpty =&obj->q2;empty =&obj->q1;}while(SLsize(noEmpty)>1){//根据top指向位置要先popSLPop(noEmpty);int x = SLPot(noEmpty);SLPush(empty,x);}SLPop(noEmpty);int Top = SLPot(noEmpty);while(!SLEmpty(empty)){//根据top指向位置要先popSLPop(empty);int x = SLPot(empty);SLPush(noEmpty,x);}return Top;
}
//取
int myQueuePeek(MyQueue* obj) {SL*qq1 = &obj->q1;SL*qq2 = &obj->q2;//直接取第一个进去的数据if(!SLEmpty(qq1)){return qq1->a[0];}else{        return qq2->a[0];}}
//判空
bool myQueueEmpty(MyQueue* obj) {return SLEmpty(&obj->q1)&&SLEmpty(&obj->q2);
}
//销毁
void myQueueFree(MyQueue* obj) {SLDestroy(&obj->q1);SLDestroy(&obj->q2);free(obj);obj = NULL;
}

 三、设计循环队列:

粗略讲解一下:循环队列

循环队列是一种线性数据结构。它也被称为“环形缓冲器”,大致就是一个队列有了空间大小,这个空间只能存出的数据是有限个,满了不能存,未满则可以继续存,相当于苍蝇馆吃饭,比较火爆,只能坐下k个人,那么就得排队,若是离开一个,就能进去一个,接着走

bf14b081bb4149cba654f13db4ca3ff9.jpeg

 

1、题目:

 e9c47ad1ff7e44e7ab8d584c01ec78de.png

 2、思路:

链表顺序表(数组)都可以实现,我用的数组,因为更简单一点:

根据上面的介绍可以知道,循环列队要一个标记首的变量(head和tail

初状态:都在头的位置,下标来看的话就是  0 的位置

561a3f3393cc48bdba993217c51e359f.png

检查队列满和空的情况:

空的情况:很简单,就是head = tail

561a3f3393cc48bdba993217c51e359f.png


满队时,tail应该指向的下一个位置,存一个数据tail后移一位,所以如下:4个数据的空间,满队时,刚好 tail = k(k表示数据个数(空间大小))要判满的话  也是tail==head才行,

tail%k==head;   但是这么做的话有问题:空也是head == tail,这不是冲突么! 

9f281058de4d47a399f2c0230ca9ed1c.png

有2种方法:任选,操作难度差不多

1、设置size ——记下数据个数

2、多创建一个空间

当满栈时,tail与head差了一步,我们写下 (tail+1)%(k+1);有点抽象,看下面

 

f857985830494420ac871ced97884091.png

给它用环来看:是不是更清晰了,头和尾的差了一个 1也就是tail+1;因为数组是一块连续的空间,所以我们要用%(k+1)将尾和头相连 

82c94e27718342fba491a17f9b21087e.png

插入数据时,需要注意tail的取值:

 先存数据再给tail = tail+1;但是tail不能一直往后走的(循环规定了循环的范围)!!!!它的取值只能是[0,k];所以要写一个tail %=(k+1);  因为x%k  (取值为:0 —— k-1)

8e3a8a6d4df2454da00195060cd22077.png

85a1a3b482494cf5bb9b70620756f659.png

 若是数据如此:tail是在下标为5的位置,后面是不能用的,所以要将tail传回到头去,保证了差一步(保证了一个空间不能用,因为我们多申请了一个,这个空间只是饰品,不能用)0c408bcde52c46c9bf9c976e1320c07e.png

 

取尾元素:

 tail的位置是下一个元素的位置,所以要用tail-1来调用,但是有坑,当tail的位置到达了下标0处,还能减掉吗???那不就是-1么,但是我们要的取的值tail-1的范围应该是[o,k]这个区间范围内

e64c62838c164f0cbbc7c2ff00a553aa.png

上面我们是  tail = tail%(k+1);——>>> tail-1 = tail%(k+1)-1;

我们要将它区间变成[0,k];  变形:

461a8fd2d36b456fbd350804d1c813c3.png

 

3、代码:

typedef struct {int *a;int head;int tail;int k;
} MyCircularQueue;
//因为在判空和判满的上面的函数需要调用他俩,所以我们要进行函数声明
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);//初始化:
MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue*pts = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if(pts==NULL){perror("malloc");return NULL;}pts->a = (int*)malloc(sizeof(int)*(k+1));if(pts->a==NULL){perror("malloc");return NULL;}pts->k = k;pts->tail = pts->head =0;return pts;
}//插入,返回真假
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//是否为满,满了就不能插入;if(myCircularQueueIsFull(obj)){return false;}else{obj->a[obj->tail]= value;obj->tail++;obj->tail%=(obj->k+1);return true;}}
//删除:返回真假
bool myCircularQueueDeQueue(MyCircularQueue* obj) {//是否为空,为空不能删除if(myCircularQueueIsEmpty(obj)){return false;}else{obj->head++;obj->head%=(obj->k+1);return true;}
}
//取头元素
int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;}else{return obj->a[obj->head];}
}
//取尾元素
int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;}else{return obj->a[(obj->tail-1+obj->k+1)%(obj->k+1)];}
}
//s是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->head == obj->tail;
}
//是否满
bool myCircularQueueIsFull(MyCircularQueue* obj) {return obj->head == (obj->tail+1)%(obj->k+1);
}
//销毁
void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);obj->a =NULL;free(obj);obj = NULL;}

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

android进阶-Binder

参考:Android——Binder机制-CSDN博客 机制:Binder是一种进程间通信的机制 驱动:Binder是一个虚拟物理设备驱动 应用层:Binder是一个能发起进程间通信的JAVA类 Binder相对于传统的Socket方式,更加高效Binder数据拷贝…

C++ VScode: launch: program ...... dose not exist

VScode: launch: program … dose not exist 介绍 参考VS Code 配置 C/C 编程运行环境(保姆级教程)教程配置了VSCode。在配置launch.json适用多个.c 文件编译时,弹出下面错误。 原因和解决方法 是task.json 默认配置的问题。 默认的 cwd参…

LearnOpenGL(十一)之光源

一、投光物 将光投射(Cast)到物体的光源叫做投光物(Light Caster)。 二、平行光 当一个光源处于很远的地方时,来自光源的每条光线就会近似于互相平行,我们可以称这些光为平行光。当我们使用一个假设光源处于无限远处的模型时,它就被称为定向…

Acrobat Pro DC 2023 for Mac:PDF处理的终极解决方案

Acrobat Pro DC 2023 for Mac为Mac用户提供了PDF处理的终极解决方案。它具备强大的文档处理能力,无论是查看、编辑还是创建PDF文件,都能轻松胜任。在编辑功能方面,Acrobat Pro DC 2023支持对文本、图像进行精准的修改和调整,还能添…

【Web后端】Tomcat简介_安装_解决乱码_idea配置

1.1 简介 tomcat是在oracle公司的ISWDK(lavaServer Web DelevopmentKit)的基础上发展起来的一个优秀的开源的servlet容器tomcat使用java语言编写。运行稳定、可靠、效率高,可以和目前 主流web服务器一起工作(如IIS、Apache、 Nginx)tomcat是Apache软件基金会(Apach…

MySQL数据库的安装和部署

1.数据库的相关介绍 关系型数据库管理系统:(英文简称:RDBMS) 为我们提供了一种存储数据的特定格式,所谓的数据格式就是表, 在数据库中一张表就称为是一种关系. 在关系型数据库中表由两部分组成&#xf…

Springboot集成SpringbootAdmin实现服务监控管理-10

SpringbootAdmin Spring Boot Admin是一个用于管理和监控Spring Boot应用程序的开源软件。 概要介绍 Spring Boot Admin可以监控Spring Boot单机或集群项目,它提供了详细的健康(Health)信息、内存信息、JVM系统和环境属性、垃圾回收信息、…

原子学习笔记5——点亮 LED

一、应用层操控设备的两种方式 应用层如何操控底层硬件,同样也是通过文件 I/O 的方式来实现,设备文件便是各种硬件设备向应用层提供的一个接口,应用层通过对设备文件的 I/O 操作来操控硬件设备,譬如 LCD 显示屏、串口、按键、摄像…

2024母亲节嘉年华主题活动方案

2024“感恩母亲,节时光请慢点”妇产医院互动嘉年华主题活动方案-33P 方案页码:33页 文件格式:pptx 方案简介: 呵护我们长大,是妈妈用青春书写过最美的诗。 五月的第二个星期日,是对妈妈说爱的日子, 她…

基于FPGA的音视频监视器,音视频接口采集器的应用

① 支持1路HDMI1路SDI 输入 ② 支持1路HDMI输出 ③ 支持1080P高清屏显示实时画面以 及叠加的分析结果 ④ 支持同时查看波形图(亮度/RGB)、 直方图、矢量图 ⑤ 支持峰值对焦、斑马纹、伪彩色、 单色、安全框遮幅标记 ⑥ 支持任意缩放画面,支…

线路和绕组中的波过程(一)

本篇为本科课程《高电压工程基础》的笔记。 本篇为这一单元的第一篇笔记。下一篇传送门。 当电路中的设备(元件)最大实际尺寸l大于人们所感兴趣的谐波波长 λ \lambda λ时,可以作为集中参数处理,否则就要当做分布参数处理。即&…

一套3D PACS系统源码:可实现医学影像获取、存档、观片、处理、打印多项应用、基于C#+VC + MSSQL开发的全套PACS源码

一套3D PACS系统源码:可实现医学影像获取、存档、观片、处理、打印多项应用 PACS的功能价值在于通过连接不同的影像设备,存储与管理图像,图像的调用与后处理,实现资源共享,降低成本,达到提高工作效率、提升…

【多客系统】社交圈子论坛系统,小程序/app/H5多端圈子社区论坛系统交友,社区圈子论坛小程序前后端搭建,社交圈平台系统

简述 社交圈子论坛系统是一种面向特定人群或特定话题的社交网络,它提供了用户之间交流、分享、讨论的平台。在这个系统中,用户可以创建、加入不同的圈子,圈子可以是基于兴趣、地域、职业等不同主题的。用户可以在圈子中发帖、评论、点赞等互…

Vulnhub靶机随笔-Hacksudo_Aliens

Vulnhub靶机Hacksudo_Aliens详解 攻击机Kali IP:192.168.3.44 靶机 IP:未知 系统:未知 A.信息收集 扫描靶机存活性 确定IP地址 1.命令:arp-scan -l 扫描靶机开放端口及其服务版本信息 2.命令 nmap -A -p- -sV 靶机IP地址 靶机开放三个端口,22ssh端口,80web端…

Qt Excel读写 - QXlsx读取Excel文件显示到QTableWidget

Qt Excel读写 - QXlsx读取Excel文件显示到QTableWidget 引言一、设计思路二、核心源码三、其他参考链接 引言 QXlsx官方显示的例子中,有一个XlsxFactory可以Load xlsx file and display on Qt widgets.但是其包含商业许可…自己写了一个简化版本:可以读取…

macOS Sonoma 无法打开分段式Dmg文件的解决办法

在macOS Sonoma 14.X及更高版本的系统中,用户可能会遇到一个棘手的问题:无法直接打开“分段式”DMG(磁盘映像)安装包文件。这种情况通常发生在尝试安装一些大型软件或游戏时,尤其是那些因为文件体积巨大而采用分段压缩…

Springboot+Vue项目-基于Java+MySQL的宠物商城网站系统(附源码+演示视频+LW)

大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:Java毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计 &…

Windows只能安装在GPT磁盘上

转换磁盘分区形式 步骤1. 先按照正常流程使用Windows系统安装光盘或系统U盘引导计算机。 步骤2. 在Windows安装程序中点击“开始安装”,然后按ShiftF10打开命令提示符。 步骤3. 依次输入以下命令,并在每一行命令后按一次Enter键执行。 步骤4. 等待转换…

2010-2030年GHS-POP数据集下载

扫描文末二维码,关注微信公众号:ThsPool 后台回复 g008,领取 2010-2030年100m分辨率GHS-POP 数据集 📊 GHS Population Grid (R2023):全球人口分布的精准视图与深度应用 🌐 在全球化和快速城市化的今天&am…

linux grep命令搜索指定路径

在Linux开发的过程中grep这个搜索命令,是必不可少的存在。它可以快速的搜索出来我们需要的关键字所在的位置。 有助于我们快速分析定位问题。 下面,分享一个简单实用的小技巧。 原始grep 最终grep grep过滤掉二进制的文件 -I选项 结论 这样子是不…