数据结构刷题训练:队列实现栈
目录
前言
1. 题目:使用队列实现栈
2. 思路
3. 分析
3.1 创建栈
3.2入栈
3.3 出栈
3.4 栈顶数据
3.5 判空和 “ 栈 ” 的销毁
4. 题解
总结
前言
我们已经学习了栈和队列,也都实现了它们各自的底层接口,那么接下我们就要开始栈和队列的专项刷题训练。
1. 题目:使用队列实现栈
题目描述:

题目链接:
队列实现栈
https://leetcode.cn/problems/implement-stack-using-queues/
2. 思路
队列的结构是先进先出,题目的要求是,让我们利用队列的底层接口来实现栈,不可以改变队列的底层逻辑,所以如果你的思路是逆置队列这个链表,那这个思路就被pass掉了。
那我们要如何利用队列尾进头出的特性来实现栈的尾进尾出呢?题目中给了我们两个队列,我们要使用这两个队列实现栈。
入栈操作好说,问题在于出栈问题,思路是这样的:我们有两个队列,一个队列用于存储数据,另外一个队列(空队列)用于拷贝数据,将原队列的前n-1个数据拷贝到空队列中,然后再将原队列剩余的最后一个元素出队列,这样就模拟实现了栈的尾出。

3. 分析
根据上述的思路分析,队列实现栈,入栈不需要什么特殊操作例如我们入栈:1、2、3、4、5,出栈呢就是:5、4、3、2、1。
上述的思路已经介绍了解决办法,也是非常简单的,但有人可能会问:那这样算法的效率岂不是很低?这种方法的效率确实低,但是这道题目考察的并不是效率的问题,而实性质问题,这也是一道经典的面试题目。这道题目并不难,但它考察对数据结构的理解,各各接口的实现中有很多需要注意的细节。
首先这道题目是并没有给现成的队列,使用C语言解决需要我们现成造轮子,这也是C语言刷题的弊端,有很多题目都需要造轮子。那么这里我们就可以直接复制前边我们实现的队列。
接下来就是我们开始注意实现接口:
首先题目中给了我们两个队列,为了便于传参和使用,我们可以定义一个结构体:
typedef struct {
Que q1; //注意这里定于的队列类型一定要与自己定义的队列结构体类型对应
Que q2;
} MyStack;
这里我们在前边介绍结构体时提到过,匿名结构体。
3.1 创建栈
MyStack* myStackCreate() {}
题目给出的接口如上,那这里我们要怎么创建我们的栈呢?是这样吗?
MyStack* myStackCreate() {MyStack st;//…return &st;
}
对函数和指针比较熟悉的同学可能就已经发现不行,为什么不行?这里就牵扯到了函数相关的知识,函数内创建的变量都是存储在栈区,出了函数就会被销毁,内存已经被销毁,返回指针还有什么意义呢?所以这里需要使用malloc函数,动态内存分配开辟的空间在堆区,程序结束前不主动释放就一直存在。所以上述的创建变量的方法不可取。
正确的方法:
MyStack* myStackCreate() {MyStack* pst=(MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);QueueInit(&pst->q2);return pst;
}
这里的pst->q1,就等价于我们在创建的队列的结构体变量:Que q;在调用接口时需要传地址过去。
3.2入栈
接下来就是入栈,题目中给了我们两个队列,为了后续出栈操作我们需要确保一个队列为空,用于拷贝数据,所以我们入栈时需要在不为空的队列入。
void myStackPush(MyStack* obj, int x) {if(!IsEmpty(&obj->q1)){QueuePush(&obj->q1,x);}else{QueuePush(&obj->q2,x);}
}
如果两个都为空那就随便选一个都可以。
3.3 出栈
在进行出栈操作的时候,我们需要判断哪一个队列为空,然后将非空队列的前n-1个元素依次拷贝到空队列当中。这里我们可以先假设队列1为空,然后在判断队列1是否为空,如果不为空那就是队列2为空,进行修改。这个假设的方法还是很实用的。
拷贝过程如下:
注意这里是拷贝,不是将原队列的节点插入到空队列,而是通过队头数据这个函数接口来将数据传过去,然后入队(调用入队接口),入队之后及时更新队头(出队)。

int myStackPop(MyStack* obj) {Que* Empty=&obj->q1;Que* NoEmpty=&obj->q2;if(!IsEmpty(&obj->q1)){Empty=&obj->q2;NoEmpty=&obj->q1;}while(QueueSize(NoEmpty)>1){QueuePush(Empty,QueueFront(NoEmpty));QueuePop(NoEmpty);}int top=QueueFront(NoEmpty);//最后保存非空队列最后一个队列节点的数据,便于返回QueuePop(NoEmpty); //最后一个元素出队。return top;
}
3.4 栈顶数据
栈顶数据接口实现就简单了,我们前边对队列进行实现时,有队头和队尾数据的接口,我们可以直接调用。
int myStackTop(MyStack* obj) {if(!IsEmpty(&obj->q1)){return QueueBlack(&obj->q1);}else{return QueueBlack(&obj->q2);}
}
3.5 判空和 “ 栈 ” 的销毁
判空就很简单,如果两个队列都为空,那么这个 “ 栈 ” 也就为空。
bool myStackEmpty(MyStack* obj) {return (IsEmpty(&obj->q1)&&IsEmpty(&obj->q2));
}
“ 栈 ”的销毁,这里就不能直接free掉obj了,如果直接释放那我们程序中的两个队列就会丢失无法释放,所以在释放掉obj之前,我们需要先将两个队列销毁。
void myStackFree(MyStack* obj) {DestoryQueue(&obj->q1);DestoryQueue(&obj->q2);free(obj);
}
4. 题解
完整代码如下:
typedef int Datatype;
typedef struct QueueNode
{struct QueueNode* next;Datatype data;}QueueNode;
typedef struct Queue
{QueueNode* head;QueueNode* tail;int size;
}Que;
//初始化队列
void QueueInit(Que* pq);
//入队
void QueuePush(Que* pq, Datatype x);
//出队
void QueuePop(Que* pq);
//队头数据
Datatype QueueFront(Que* pq);
//队尾数据
Datatype QueueBlack(Que* pq);
//判空
bool IsEmpty(Que* pq);
//队列大小
int QueueSize(Que* pq);
//销毁队列
void DestoryQueue(Que* pq);void QueueInit(Que* pq)
{assert(pq);pq->head = pq->tail = NULL;pq->size = 0;
}
void QueuePush(Que* pq, Datatype x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc");exit(-1);}newnode->data = x;newnode->next = NULL;if (pq->tail == NULL){pq->head = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}pq->size++;
}
void QueuePop(Que* pq)
{assert(pq);assert(!IsEmpty(pq));if (pq->head->next == NULL){free(pq->head);pq->head = pq->tail = NULL;}else{QueueNode* next = pq->head->next;free(pq->head);pq->head = next;}pq->size--;
}
Datatype QueueFront(Que* pq)
{assert(pq);assert(!IsEmpty(pq));return pq->head->data;
}
Datatype QueueBlack(Que* pq)
{assert(pq);assert(!IsEmpty(pq));return pq->tail->data;
}
bool IsEmpty(Que* pq)
{assert(pq);return (pq->head == NULL);
}
int QueueSize(Que* pq)
{assert(pq);return pq->size;
}
void DestoryQueue(Que* pq)
{assert(pq);QueueNode* cur = pq->head;while (cur){QueueNode* next = cur->next;free(cur);cur = next;}pq->head = pq->tail = NULL;pq->size = 0;
}
typedef struct {
Que q1;
Que q2;
} MyStack;MyStack* myStackCreate() {MyStack* pst=(MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);QueueInit(&pst->q2);return pst;
}void myStackPush(MyStack* obj, int x) {if(!IsEmpty(&obj->q1)){QueuePush(&obj->q1,x);}else{QueuePush(&obj->q2,x);}
}int myStackPop(MyStack* obj) {Que* Empty=&obj->q1;Que* NoEmpty=&obj->q2;if(!IsEmpty(&obj->q1)){Empty=&obj->q2;NoEmpty=&obj->q1;}while(QueueSize(NoEmpty)>1){QueuePush(Empty,QueueFront(NoEmpty));QueuePop(NoEmpty);}int top=QueueFront(NoEmpty);QueuePop(NoEmpty);return top;
}int myStackTop(MyStack* obj) {if(!IsEmpty(&obj->q1)){return QueueBlack(&obj->q1);}else{return QueueBlack(&obj->q2);}
}bool myStackEmpty(MyStack* obj) {return (IsEmpty(&obj->q1)&&IsEmpty(&obj->q2));
}void myStackFree(MyStack* obj) {DestoryQueue(&obj->q1);DestoryQueue(&obj->q2);free(obj);
}
总结
本文队列模拟实现栈,让我们在实践中深入思考了数据结构的本质和应用,为我们的编程能力和问题解决能力提供了锻炼。本期内容到此结束,感谢阅读!
相关文章:
数据结构刷题训练:队列实现栈
目录 前言 1. 题目:使用队列实现栈 2. 思路 3. 分析 3.1 创建栈 3.2入栈 3.3 出栈 3.4 栈顶数据 3.5 判空和 “ 栈 ” 的销毁 4. 题解 总结 前言 我们已经学习了栈和队列,也都实现了它们各自的底层接口,那么接下我们就要开始栈和队列的专项刷…...
(统计学习方法|李航)第四章 朴素贝叶斯算法——贝叶斯估计
贝叶斯估计方法: 计算男女时只有两个值,所以K2 贝叶斯估计就是拉普拉斯平滑 估计方法:为什么叫做贝叶斯估计呢? 例题: 重新回顾以下朴素贝叶斯: 对他求导,求出最大值 得到了色i他的估计值&…...
企业直播MR虚拟直播(MR混合现实直播技术)视频介绍
到底什么是企业直播MR虚拟直播(MR混合现实直播技术)? 企业直播MR虚拟直播新玩法(MR混合现实直播技术) 我的文章推荐: [视频图文] 线上研讨会是什么,企业对内对外培训可以用线上研讨会吗&#x…...
React Fiber: 从 Reconciliation 到 Concurrent Mode
React Fiber 是 React 中的一种新的协调算法,它的主要目的是提高 React 的性能和可维护性。在 React Fiber 之前,React 使用了一种叫做 Stack Reconciliation 的算法来处理组件的更新和渲染。但是 Stack Reconciliation 存在一些问题,比如无法…...
【PostgreSQL内核学习(十一)—— OpenGauss源码学习(CopyTo)】
可优化语句执行 概述什么是列存储?列存的优势 相关函数CopyToCStoreCopyToCopyStatetupleDescCStoreScanDesc CStoreBeginScanRelationSnapshotProjectionInfo GetCStoreNextBatchRunScanFillVecBatchCStoreIsEndScan CStoreEndScan 声明:本文的部分内容…...
计算机网络 网络层 IPv4地址
A类地址第一位固定0 B类10 其下同理...
【程序员社交】多和高层次人群交流
定义问题:如何多和高层次人群交流获取经验提升自己? 收集信息:通过社交媒体、行业论坛、行业大会等途径获取高层次人群的信息和观点,并了解他们的工作经历、技能和能力。 分析信息:分析收集到的信息,了解…...
机器学习笔记 - 基于C++的深度学习 三、实现成本函数
机器学习中的建模 作为人工智能工程师,我们通常将每个任务或问题定义为一个函数。 例如,如果我们正在开发面部识别系统,我们的第一步是将问题定义为将输入图像映射到标识符的函数F(X)。但是问题是如何知道F(X)公式? 事实上,使用公式或一系列固有规则来定义F(X)是不可行的(…...
lazada、shopee店铺如何利用测评提高权重和排名?
在 lazada、shopee平台上开店后,卖家们必须对店铺的权重进行更多的关注。如果店铺的权重越高,那么它就会带来更多的流量和更多的订单,那么在 lazada、shopee平台上开设一家店铺,该怎样增加它的店铺权重和排名呢? laza…...
安全第二次
一,iframe <iframe>标签用于在网页里面嵌入其他网页。 1,sandbox属性 如果嵌入的网页是其他网站的页面,因不了解对方会执行什么操作,因此就存在安全风险。为了限制<iframe>的风险,HTML 提供了sandb…...
125、SpringBoot可以同时处理多少请求?
SpringBoot可以同时处理多少请求? 一、前言二、线程池4大参数图解三、代码示例一、前言 我们都知道,SpringBoot默认的内嵌容器是Tomcat,也就是我们的程序实际上是运行在Tomcat里的。所以与其说SpringBoot可以处理多少请求,到不如说Tomcat可以处理多少请求。 关于Tomcat的默…...
SSE技术和WebSocket技术实现即时通讯
文章目录 一、SSE1.1 什么是SSE1.2 工作原理1.3 特点和适用场景1.4 API用法1.5 代码实现 二、WebSocket2.1 什么是WebSocket2.2 工作原理2.3 特点和适用场景2.4 API用法2.5 代码实现2.6 心跳检测 三、SSE与WebSocket的比较 当涉及到实现实时通信的Web应用程序时,两种…...
什么是敏捷开发?
敏捷开发流程:制度化、规范化地PUA程序员的顶级神器!!!...
tcp发送整型,结构体等数据的方法
测试环境 Receiver: x86 UbuntuSender: arm64 android 发送整型数 C语言和套接字库来发送一个整型变量(int)的客户端程序。 它首先创建一个TCP套接字,然后连接到指定的服务器地址和端口。接着,它将一个整型变量(in…...
【Unity每日一记】让一个物体按余弦曲线移动—(三角函数的简单运用)
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:uni…...
python爬虫实战——数据可视化
本篇文章将介绍如何利用Python爬虫获取数据并进行可视化展示,包括以下主要内容: 数据获取:使用requests库发送HTTP请求获取目标网页的数据;数据解析:使用BeautifulSoup库对HTML代码进行解析提取所需数据;数…...
案例13 Spring MVC参数传递案例
基于Spring MVC实现HttpServletRequest、基本数据类型、Java Bean、数组、List、Map、JSON方式的参数传递。 1. 创建项目 选择Maven快速构建web项目,项目名称为case13-springmvc02。 2. 配置Maven依赖 <?xml version"1.0" encoding"UTF-8&quo…...
IntellIJ Idea 连接数据库-MySql
前言:可以用mariaDB工具,在本地创建服务器主机和数据库,而后用intellIJ Idea尝试连接 MariaDB创建数据库练习 1.IntellIJ Idea打开界面右侧Database工具,选择MySQL数据库。 2.填写数据库账号密码,地址端口号ÿ…...
通讯协议036——全网独有的OPC HDA知识一之聚合(五)计数
本文简单介绍OPC HDA规范的基本概念,更多通信资源请登录网信智汇(wangxinzhihui.com)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化,以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&…...
【TensorFlow】P0 Windows GPU 安装 TensorFlow、CUDA Toolkit、cuDNN
Windows 安装 TensorFlow、CUDA Toolkit、cuDNN 整体流程概述TensorFlow 与 CUDA ToolkitTensorFlow 是一个基于数据流图的深度学习框架CUDA 充分利用 NIVIDIA GPU 的计算能力CUDA Toolkit cuDNN 安装详细流程整理流程一:安装 CUDA Toolkit步骤一:获取CU…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
