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

【LeetCode数据结构】栈和队列的应用——用队列实现栈问题、用栈实现队列问题详解


🔥个人主页:艾莉丝努力练剑

专栏传送门:《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题

🍉学习方向:C/C++方向

⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平


 


前言:牛客网和LeetCode的刷题都不可或缺,我们都要做一做,无论是参加竞赛还是笔试面试,至少能提升你的代码能力!洛谷的题目也可以去做一做。力扣的题目对提升代码能力很有帮助,需要有一点基础,几乎都是接口型的题目,关于接口型和IO型的区别我们在本专栏的第一篇【LeetCode】力扣题——轮转数组、消失的数字、数组串联中就介绍过了,这里不再赘述,我们进入今天的力扣题目介绍——


目录

正文

一、用队列实现栈问题

(一)思路

(二)解题过程

1、初始化

2、入栈

3、出栈

4、取栈顶元素

 5、判断栈是否为空

6、销毁栈

(三)完整代码

二、用栈实现队列问题

(一)思路 

(二)解题过程 

1、初始化

2、入队列

3、出队列

4、取队头

5、判断为空

6、销毁

(三)完整代码

结尾


正文

一、用队列实现栈问题

225.用队列实现栈

博主题解链接:调用队列结构实现栈

题目描述:

题目示例和提示——  

(一)思路

我们的思路是:入栈:往不为空的队列中插入数据;出栈:把不为空队列中前size-1个数据挪到另一个队列,再将最后一个数据出队列;取栈顶(不出数据):找不为空的队尾数据,返回队尾数据(我们之前实现过取队尾操作)。如下图——

(二)解题过程

既然是用队列实现栈问题,那我们直接调用队列。

1、初始化

前面已经调用了队列的头文件和各种方法,我们就不用自己写了。

//创建一个栈
MyStack* myStackCreate()
{MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);//把q1的地址传过去QueueInit(&pst->q2);return pst;
}
2、入栈

入栈的代码——

//往栈里面插入一个栈顶元素
void myStackPush(MyStack* obj, int x) 
{//往不为空的队列里面插入数据if!(QueueEmpty(&obj->q1))//传q1的地址{//q1QueuePush(&obj->q1,x);}else{//q2QueuePush(&obj->q2,x);}
}
3、出栈
    //不为空队列前size-1个数据挪到空队列中while(QueueSize(&noneEmp) > 1){//取队头int front = QueueFront(&noneEmp);//入另一个队列QueuePush(&emp,front);//出队头QueuePop(&noneEmp)}

这里可以合并一下,反正返回值要作为第二个方法的front,那这里我们其实可以不创建临时变量,我们直接让返回值作为第二个参数——先取队头,再入到另一个队列当中——

//取队头,入另一个队列
QueuePush(&emp,QueueFront(&noneEmp));
//出队头
QueuePop(&noneEmp)

 出栈的代码——

//往栈里面删除一个栈顶元素
int myStackPop(MyStack* obj) 
{//找不为空队列//类似于前面假设长短链表的方法,这里假设空和非空Queue* emp = &obj->q1;Queue* noneEmp = &obj->q2;if(QueueEmpty(&obj->q2))//这里是不加!的{emp = &obj->q2;noneEmp = &obj->q1;}//不为空队列前size-1个数据挪到空队列中while(QueueSize(&noneEmp) > 1){//取队头,入另一个队列QueuePush(&emp,QueueFront(&noneEmp));//出队头QueuePop(&noneEmp)}//不为空队列数据出队int top = QueueFront(&noneEmp);QueuePop(&noneEmp);return top;
}

返回top值——

4、取栈顶元素
//取栈顶元素
int myStackTop(MyStack* obj) 
{//找不为空队列,返回不为空队列的队尾数据if(!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{return QueueBack(&obj->q2);}
}
 5、判断栈是否为空
//判断栈是否为空
bool myStackEmpty(MyStack* obj) 
{return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
6、销毁栈
//销毁栈
void myStackFree(MyStack* obj) 
{QueueDestory(&obj->q1);QueueDestory(&obj->q2);free(obj);obj = NULL;
}

(三)完整代码

typedef int QDataType;
//定义节点的结构typedef struct QueueNode 
{QDataType data;struct QueueNode* next;
}QueueNode;//定义队列的结构
typedef struct Queue 
{QueueNode* phead;//指向队头节点的指针QueueNode* ptail;//指向队尾节点的指针int size;		   //队列中有效数据个数
}Queue;//初始化
void QueueInit(Queue* pq);//销毁
void QueueDestory(Queue* pq);//入队列,队尾
void QueuePush(Queue* pq, QDataType x);// 出队列,队头
void QueuePop(Queue* pq);//队列判空
bool QueueEmpty(Queue* pq);//取队头数据
QDataType QueueFront(Queue* pq);//取队尾数据
QDataType QueueBack(Queue* pq);//队列有效元素个数
int QueueSize(Queue* pq);//初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}//销毁队列
void QueueDestory(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while(pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;//防止phead、ptail变野指针pq->size = 0;//销毁后元素个数重置为0
}//入队列,队尾
void QueuePush(Queue* pq, QDataType x)
{int size = 0;assert(pq);//创建值为x的节点QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;//队列为空if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = pq->ptail->next;}pq->size++;//入队后元素个数+1
}//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}// 出队列,队头
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));//队列中只有一个节点if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else {QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}pq->size--;//出队后元素个数-1
}//取队头数据
QDataType QueueFront(Queue* pq)
{ assert(!QueueEmpty(pq));return pq->phead->data;
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->ptail->data;
}//队列有效元素个数
int QueueSize(Queue* pq)
{/*assert(pq);QueueNode* pcur = pq->phead;int size = 0;while (pcur){++size;pcur = pcur->next;}return size;*/return pq->size;
}
//--------------------以上是队列的结构和常用方法----------------------//两个队列实现一个栈
typedef struct
{Queue q1;Queue q2;
} MyStack;//创建一个栈
MyStack* myStackCreate()
{MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);//把q1的地址传过去QueueInit(&pst->q2);return pst;
}
//往栈里面插入一个栈顶元素
void myStackPush(MyStack* obj, int x) {//往不为空的队列里面插入数据if(!QueueEmpty(&obj->q1))//传q1的地址{//q1QueuePush(&obj->q1, x);}else {//q2QueuePush(&obj->q2, x);}
}
//往栈里面删除一个栈顶元素
int myStackPop(MyStack* obj) {//找不为空队列//类似于前面假设长短链表的方法,这里假设空和非空Queue* emp = &obj->q1;Queue* noneEmp = &obj->q2;if (QueueEmpty(&obj->q2))//这里是不加!的{emp = &obj->q2;noneEmp = &obj->q1;}//不为空队列前size-1个数据挪到空队列中while (QueueSize(noneEmp) > 1)//这里不用取地址,本身就已经是地址了{//取队头,入另一个队列QueuePush(emp, QueueFront(noneEmp));//出队头QueuePop(noneEmp);}//不为空队列数据出队int top = QueueFront(noneEmp);QueuePop(noneEmp);return top;
}
//取栈顶元素
int myStackTop(MyStack* obj) {//找不为空队列,返回不为空队列的队尾数据if(!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{return QueueBack(&obj->q2);}
}
//判断栈是否为空
bool myStackEmpty(MyStack* obj) {return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
//销毁栈
void myStackFree(MyStack* obj) {QueueDestory(&obj->q1);QueueDestory(&obj->q2);free(obj);obj = NULL;
}/*** Your MyStack struct will be instantiated and called as such:* MyStack* obj = myStackCreate();* myStackPush(obj, x);* int param_2 = myStackPop(obj);* int param_3 = myStackTop(obj);* bool param_4 = myStackEmpty(obj);* myStackFree(obj);
*/

二、用栈实现队列问题

232.用栈实现队列

博主题解链接:三步走方法用栈实现队列

题目描述:

题目示例和提示——  

(一)思路 

题目分析:

我们先画一下看看——

思路1:  

思路2:  

因此,我们的思路还是三步走: 

(二)解题过程 

1、初始化
//初始化
MyQueue* myQueueCreate() 
{MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));//栈的初始化STInit(&pq->pushST);//STInit的形参是一个一级指针,要传地址STInit(&pq->popST);return pq;
}
2、入队列
//入队列
void myQueuePush(MyQueue* obj, int x) 
{//往pushST中插入数据STPush(&obj->pushST);
}
3、出队列

刚才我们分析过了,出队列要判断popST为不为空——

//出队列——要判断popST为不为空
int myQueuePop(MyQueue* obj) 
{//popST为空——将pushST(不为空)导入到popSTif(STEmpty(&obj->popST){while(!STEmpty(&obj->pushST)){//取栈顶(pushST),入popST栈,出pushST栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接出int top = STTop(&obj->popST);STPop(&obj->popST);return top;
}
4、取队头

取队头其实和出队列几乎一样,只不过取队头不用删除数据——

//取队头
int myQueuePeek(MyQueue* obj) 
{//popST为空——将pushST(不为空)导入到popSTif(STEmpty(&obj->popST)){while(!STEmpty(&obj->pushST)){//取栈顶(pushST),入popST栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接取数据int top = STTop(&obj->popST);return top;
}
5、判断为空

两个栈为空,才能为空——

//判断是否为空
bool myQueueEmpty(MyQueue* obj) 
{return STEmpty(&obj->popST) && STEmpty(&obj->pushST);
}
6、销毁

对队列进行销毁操作:

1、初始化那儿有个指向队列的指针是malloc出来的,得销毁;

2、在队列里面有两个栈也是向操作空间malloc出来的,也要销毁。

void myQueueFree(MyQueue* obj) 
{STDestory(&obj->pushST);STDestory(&obj->popST);free(obj);obj = NULL;
}

(三)完整代码

typedef int STDataType;
typedef struct Stack
{STDataType* arr;int top;//定义栈中有效的数据个数int capacity;//栈的空间大小
}ST;//初始化
void STInit(ST* ps)
{ps->arr = NULL;ps->top = ps->capacity = 0;
}//销毁
void STDestory(ST* ps)
{if (ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}//入栈——栈顶
void STPush(ST* ps, STDataType x)
{assert(ps);//判断空间是否足够if (ps->top == ps->capacity){//增容int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}ps->arr = tmp;ps->capacity = newCapacity;}//空间足够ps->arr[ps->top++] = x;
}//栈是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}//出栈——栈顶
void STPop(ST* ps)
{assert(!STEmpty(ps));ps->top--;
}//取栈顶元素
STDataType STTop(ST* ps)
{assert(!STEmpty(ps));return ps->arr[ps->top - 1];
}//获取栈中有效元素个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}
//-----------------以上栈的结构定义和常见的方法----------------------
typedef struct {ST pushST;ST popST;
} MyQueue;//初始化
MyQueue* myQueueCreate() {MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));//栈的初始化STInit(&pq->pushST);//STInit的形参是一个一级指针,要传地址STInit(&pq->popST);return pq;
}
//入队列
void myQueuePush(MyQueue* obj, int x) {//往pushST中插入数据STPush(&obj->pushST,x);
}
//出队列——要判断popST为不为空
int myQueuePop(MyQueue* obj) {//popST为空——将pushST(不为空)导入到popSTif(STEmpty(&obj->popST)){while(!STEmpty(&obj->pushST)){//取栈顶(pushST),入popST栈,出pushST栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接出int top = STTop(&obj->popST);STPop(&obj->popST);return top;
}
//取队头
int myQueuePeek(MyQueue* obj) {//popST为空——将pushST(不为空)导入到popSTif(STEmpty(&obj->popST)){while(!STEmpty(&obj->pushST)){//取栈顶(pushST),入popST栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接取数据int top = STTop(&obj->popST);return top;
}
//判断为空
bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->popST) && STEmpty(&obj->pushST);
}
//对队列进行销毁操作
//1.初始化那儿有个指向队列的指针是malloc出来的,得销毁
//2.在队列里面有两个栈也是向操作空间malloc出来的,也要销毁
void myQueueFree(MyQueue* obj) {STDestory(&obj->pushST);STDestory(&obj->popST);free(obj);obj = NULL;
}/*** Your MyQueue struct will be instantiated and called as such:* MyQueue* obj = myQueueCreate();* myQueuePush(obj, x);* int param_2 = myQueuePop(obj);* int param_3 = myQueuePeek(obj);* bool param_4 = myQueueEmpty(obj);* myQueueFree(obj);
*/

结尾

往期回顾:

【LeetCode&数据结构】栈的应用——有效的括号问题详解

由于本专栏的篇数越来越多,为了避免文章链接挂太多影响观感,博主之后的每一篇力扣刷题详解都只会附上前一篇的链接,最后一次完整链接是在之后会发布的【栈的应用——有效的括号问题详解】和【单链表的应用——环形链表问题详解】的文章结尾,本意是为了方便大家找到相应的详细的题解,现在文章多了,铸币博主没办法一一罗列,而且还会有“凑字数”的嫌疑,力扣刷题专栏的链接每次都会放在文章开头的位置,大家可自行前往!

       感谢大家的理解与支持!

【LeetCode&数据结构】单链表的应用——环形链表问题详解

结语:本篇文章到这里就结束了,本文讲述的两道代码题并不适合C语言初学者,需要有一定的C语言基础,最好要学过数据结构与算法的算法复杂度和链表的知识,才能写出复杂度较优的代码来。大家一定要自己动手敲一敲,不敲的话不仅容易忘记,也不方便将来复习。

http://www.dtcms.com/a/292148.html

相关文章:

  • Leetcode力扣解题记录--第73题(矩阵置零)
  • ecflow服务的搭建,源码编译方式
  • Gerrit多仓库对应多邮箱配置办法
  • Stirling PDF本地PDF编辑器:cpolar内网穿透实验室第628个成功挑战
  • Apache Ignite 索引(Indexes)定义和使用
  • 【运维】SGLang服务器参数配置详解
  • Vue开发常用库(含npm安装命令)
  • IPv4枯竭时代:从NAT技术到IPv6的演进之路
  • C++函数 vs Go函数
  • C++ 中重载函数右值引用和左值引用匹配的优先级
  • 【机器学习】第四章 回归算法
  • 主流开源LLM架构对比与突破·
  • 控制器(Controller)模块的架构与工作流程 -OpenExo
  • Azure可靠性架构指南:构建云时代的高可用系统
  • Java函数指南:从Function到BiFunction的深度解析
  • 电商项目_秒杀_架构及核心
  • 【Linux】进程概念
  • 以Java程序员角度理解MCP
  • 项目复盘核心要点
  • SQL Server 查询优化
  • Vue 脚手架基础特性
  • 前端自动化埋点:页面模块级行为跟踪与问题定位系统​​的技术设计方案
  • 浏览器地址栏输入URL回车后白屏分析
  • 【Web APIs】JavaScript 节点操作 ④ ( 节点操作案例 - 下拉菜单案例 )
  • Java+Vue构建的固定资产内控管理系统,融合移动端便捷与后台管理强大功能,模块完备,提供全量源码,轻松实现资产智能管控
  • 第十二章 用Java实现JVM之结束
  • 学习pwn需要的基本汇编语言知识
  • 漫画版:细说金仓数据库
  • LangGraph基础教程
  • 理解后端开发中的API设计原则