与队列和栈相关的【OJ题】
✨✨✨专栏:数据结构
🧑🎓个人主页:SWsunlight
目录
一、用队列实现栈:
1、2个队列的关联起来怎么由先进先出转变为先进后出:(核心)
2、认识各个函数干嘛用的:
3、代码实现:
二、用栈实现队列:
1、题目:
2、思路:
3、代码:
三、设计循环队列:
1、题目:
2、思路:
编辑
3、代码:
一、用队列实现栈:
内容如下:
2个队列实现栈::首先考虑的是 栈的规则:先进后出(后进先出)
队列的规则:先进先出
1、2个队列的关联起来怎么由先进先出转变为先进后出:(核心)
如上图,外围框架(虚构的,为了更好理解,让他具体化)就是我要实现的栈,而我要通过2个队列来实现栈,是不是是可以让小球先走上面的“通道”进去,全部小球(元素)进入上“通道”中,此时我排在通道最后的小球(元素)就是我要出栈的第一个数据。
如下图:2号球要第一个出栈,我们发现下“通道”是空的,那么我让2号球前面的球离开这个“通道”去到下面的通道,上通道就会剩下一个2号球,此时取2号球顺利的第一个走出去,就相当于栈的Top(将后进的元素取出)
反复进行,就可以实现栈了,(队尾的元素能出栈,其他位置得绕着2个通道来回转(有点像明知她(他)不爱你,你还是要困死再这颗树),只有自己在成为队尾了(心灰意冷了),才会幡然醒悟(不能再一直停留再原地绕圈了,要向前看啦)
2、认识各个函数干嘛用的:
可能只是对我而言
我已经标好了,各个函数的功能:知道功能就好实现了
这个结构体的成员放2个队列即可:
结构体MyStack嵌套2个(队列)结构体(Queue)——>Queue的结构体在嵌套节点的结构体
如下:头节点后面还会链接更多的尾节点
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、题目:
2
2、思路:
和上面思路大差不差
先画图:和上面说的类似,不做赘述
区别1:栈的top指向的是 栈顶元素 还是 栈顶元素下一个位置
根据之前我写的top指向的是栈顶元素的下一个位置叙说:如下
没有数据是top = 0;当存入一个时top = 1;
所以你再取数据放到空的栈时应该也要注意先pop一下再取
还有一个需要注意的点:它比队列实现栈跟倔强(醒悟的更慢)!!!
一个栈里面的数据如下:
4 3 2 取出放到下面的空栈中
流程图如下:1 就顺利走了,以为这样就完事了??一开始我也这样想的,结果碰壁了
将1取走后,根据之前的思路,再pop时就会将3 2放到上面栈,那么最后出队顺序变成了:1 4 2 3
乱套了
非常不对劲
我想到了再回转一次,这样就可以保证我开始入队时的顺序不变,也就是将上面的栈作为出数据用,每次出一次数据,就将其他数据入栈到下面的栈,出完再回到上面的栈,无论你
可以理解为:不撞南墙不回头(心死了,才向前)!!!!头比较硬哈,越靠后撞的次数越多
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个人,那么就得排队,若是离开一个,就能进去一个,接着走
1、题目:
2、思路:
有链表和顺序表(数组)都可以实现,我用的数组,因为更简单一点:
根据上面的介绍可以知道,循环列队要一个标记首和尾的变量(head和tail)
初状态:都在头的位置,下标来看的话就是 0 的位置
检查队列满和空的情况:
空的情况:很简单,就是head = tail
满队时,tail应该指向的下一个位置,存一个数据tail后移一位,所以如下:4个数据的空间,满队时,刚好 tail = k(k表示数据个数(空间大小))要判满的话 也是tail==head才行,
tail%k==head; 但是这么做的话有问题:空也是head == tail,这不是冲突么!
有2种方法:任选,操作难度差不多
1、设置size ——记下数据个数
2、多创建一个空间
当满栈时,tail与head差了一步,我们写下 (tail+1)%(k+1);有点抽象,看下面
给它用环来看:是不是更清晰了,头和尾的差了一个 1也就是tail+1;因为数组是一块连续的空间,所以我们要用%(k+1)将尾和头相连
插入数据时,需要注意tail的取值:
先存数据再给tail = tail+1;但是tail不能一直往后走的(循环规定了循环的范围)!!!!它的取值只能是[0,k];所以要写一个tail %=(k+1); 因为x%k (取值为:0 —— k-1)
若是数据如此:tail是在下标为5的位置,后面是不能用的,所以要将tail传回到头去,保证了差一步(保证了一个空间不能用,因为我们多申请了一个,这个空间只是饰品,不能用)
取尾元素:
tail的位置是下一个元素的位置,所以要用tail-1来调用,但是有坑,当tail的位置到达了下标0处,还能减掉吗???那不就是-1么,但是我们要的取的值tail-1的范围应该是[o,k]这个区间范围内
上面我们是 tail = tail%(k+1);——>>> tail-1 = tail%(k+1)-1;
我们要将它区间变成[0,k]; 变形:
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;}
相关文章:

与队列和栈相关的【OJ题】
✨✨✨专栏:数据结构 🧑🎓个人主页:SWsunlight 目录 一、用队列实现栈: 1、2个队列的关联起来怎么由先进先出转变为先进后出:(核心) 2、认识各个函数干嘛用的: …...
Unity编辑器扩展
Unity编辑器扩展是指为Unity引擎开发者提供的一种扩展功能,可以增强Unity编辑器的功能和效能。这些扩展可以帮助开发者提高工作效率,简化工作流程,并提供更好的用户体验。本文将介绍Unity编辑器扩展的基本概念、开发流程以及一些常见的应用示…...
【kettle】kettle访问数据库系列文章及视频地址(更新中)
1.一直以来想写下基于kettle的系列文章,作为较火的数据ETL工具,也是日常项目开发中常用的一款工具,最近刚好挤时间梳理、总结下这块儿的知识体系。 2.这里整理了kettle访问数据库系列文章及视频地址整体链接,并及时补充、更新相关…...

共赴科技盛会“2024南京智博会”11月在南京国际博览中心召开
2024年,南京这座历史悠久的文化名城迎来了一场科技与智慧交织的盛会——南京智博会|南京国际智慧城市、物联网、大数据。本次博览会以智慧城市、人工智能、消费电子、物联网、大数据为主题,汇聚了全球各地的智能科技精英,共同探讨智慧城市建设…...

刷代码随想录有感(62):修建二叉搜索树
题干: 代码: class Solution { public:TreeNode* traversal(TreeNode* root, int low, int high){if(root NULL)return NULL;if(root->val < low)return traversal(root->right, low, high);if(root->val > high)return traversal(ro…...

AVL树的旋转
目录 1.平衡因子 2.旋转 a.节点定义 b.插入 插入 平衡因子更新 旋转 左单旋 右单旋 右左双旋 左右双旋 3.AVL树的验证 1.平衡因子 我们知道搜索二叉树有缺陷,就是不平衡,比如下面的树 什么是搜索树的平衡?就是每个节点的左右子树的…...
C++(动态规划之拆分整数)
其实我交上去都有点似懂非懂 题目:(343. 整数拆分 - 力扣(LeetCode)) 给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k > 2 ),并使这些整数的乘积最大化。 返回 …...
unix C之环境变量
什么是环境变量 每个进程都有自己的一张环境变量表,表中的每个条目都是形如 keyvalue 的键值对形式的环境变量。 进程可以通过环境变量访问计算机资源。 在终端下输入env命令,可以查看环境变量列表。 通过echo $name 可以查看某个环境变量的值。 环…...

Flutter实战记录-协作开发遇到的问题
一.前言 Android项目使用了混合架构,部分模块使用Flutter进行开发。在电脑A上开发的项目提交到git仓库,电脑B拉取后进行操作,遇到两个问题,特此做一下记录; 二.问题A Settings file ‘D:\xxx\settings.gradle’ line…...

Linux 安装JDK和Idea
安装JDK 下载安装包 下载地址: Java Downloads | Oracle (1) 使用xshell 上传JDK到虚拟机 (2) 移动JDK 包到/opt/environment cd ~ cd /opt sudo mkdir environment # 在 /opt下创建一个environment文件夹 ls# 复制JDK包dao /opt/environment下 cd 下载 ls jd…...

c#绘制渐变色的Led
项目场景: c#绘制渐变色的button using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using static System.Windows.Forms.AxHost;namespace WindowsFormsApp2 {public class Gradie…...

LifeCycle之ProcessLifeCycleOwner
问题:想要知道应用程序当前处在前台、后台、或从后台回到前台,想要知道应用的状态, LifeCycle提供了ProcessLifeCycleOwner的类,方便我们知道整个应用程序的生命周期情况 ProcessLifeCycleOwner 使用方法 1.首先添加依赖 imple…...

C++ | Leetcode C++题解之第79题单词搜索
题目: 题解: class Solution { public:bool exist(vector<vector<char>>& board, string word) {rows board.size();cols board[0].size();for(int i 0; i < rows; i) {for(int j 0; j < cols; j) {if (dfs(board, word, i, …...

如何通过PHP语言实现远程控制空调
如何通过PHP语言实现远程控制空调呢? 本文描述了使用PHP语言调用HTTP接口,实现控制空调,通过不同规格的通断器,来控制不同功率的空调的电源。 可选用产品:可根据实际场景需求,选择对应的规格 序号设备名称…...

【AI+换脸换装】从OpenAI 探索色情露骨内容领域浅聊AI换脸换装
5月9日消息,据外电报道,OpenAI 周三发布了文档草案,阐述了它希望 ChatGPT 及其其他人工智能技术如何运作。冗长的Model Spec 文件的一部分透露,该公司正在探索进军色情和其他露骨内容领域。 看完这个,心里有点惊讶&am…...

Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗
Flutter笔记 Widgets Easier组件库(13)使用底部弹窗 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this …...

RobbitMQ基本消息队列的消息发送过程
RabbitMQ: One broker to queue them all | RabbitMQ RabbitMQ官网 SpringAmqp的官方地址:Spring AMQP 代码示例:对着代码看应该能看明白 publisher:消息发送者的代码示例 package cn.itcast.mq.helloworld;import com.rabbitmq.client.Channel; import com.rabb…...
MongoDB聚合运算符:$topN
MongoDB聚合运算符:$topN 文章目录 MongoDB聚合运算符:$topN语法用法关于null和缺失值的处理BSON数据类型排序 举例查找三个得分最高的查找全部游戏中三个最高的得分基于分组key来计算参数n $topN聚合运算符返回分组中指定顺序的最前面 n个元素…...
什么是顶级域名、二级域名、三级域名?
什么是顶级域名、二级域名、三级域名? 一般域名都由两部分组成,中间用“.”隔开,一个域名是几级域名,简单的可以通过数“.”的方式来判断。 如baidu.com,它是由baidu和后缀“.com”组成,我们可以认定它是顶…...

[Android]四大组件简介
在 Android 开发中,“四大组件”(Four Major Components)是指构成 Android 应用程序的四种核心组件,它们通过各自的方式与系统交互,实现应用的多样功能。这些组件是:Activity、Service、Broadcast Receiver…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...

UE5 音效系统
一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类,将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix,将上述三个类翻入其中,通过它管理每个音乐…...
验证redis数据结构
一、功能验证 1.验证redis的数据结构(如字符串、列表、哈希、集合、有序集合等)是否按照预期工作。 2、常见的数据结构验证方法: ①字符串(string) 测试基本操作 set、get、incr、decr 验证字符串的长度和内容是否正…...

如何使用CodeRider插件在IDEA中生成代码
一、环境搭建与插件安装 1.1 环境准备 名称要求说明操作系统Windows 11JetBrains IDEIntelliJ IDEA 2025.1.1.1 (Community Edition)硬件配置推荐16GB内存50GB磁盘空间 1.2 插件安装流程 步骤1:市场安装 打开IDEA,进入File → Settings → Plugins搜…...

Continue 开源 AI 编程助手框架深度分析
Continue 开源 AI 编程助手框架深度分析 一、项目简介 Continue 是一个模块化、可配置、跨平台的开源 AI 编程助手框架,目标是让开发者能在本地或云端环境中,快速集成和使用自定义的 LLM 编程辅助工具。它通过支持 VS Code 与 JetBrains 等主流 IDE 插件…...

android关于pthread的使用过程
文章目录 简介代码流程pthread使用hello_test.cppAndroid.bp 编译过程报错处理验证过程 简介 android开发经常需要使用pthread来编写代码实现相关的业务需求 代码流程 pthread使用 需要查询某个linux函数的方法使用,可以使用man 函数名 // $ man pthread_crea…...