力扣225 - 用队列实现栈【C/C++实现】

news2024/9/25 9:40:46

用队列实现栈~

  • 一、题目描述
  • 二、思路分析
    • 1、结构声明与展开剖析
    • 2、入栈【入队思想】
    • 3、出栈【出队思想】
    • 4、获取栈顶元素【队列末尾】
    • 5、逐步算法图解
  • 三、整体代码展示
    • 💻C语言代码实现
    • 💻C++代码实现
  • 【⭐】补充:单队列实现栈
  • 四、总结与提炼

一、题目描述

在这里插入图片描述

示例 1:

输入
[“MyStack”, “push”, “push”, “top”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出
[null, null, null, 2, 2, false]
解释
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

提示

  • 1 <= x <= 9
  • 最多调用100 次 push、pop、top 和 empty
  • 每次调用 pop 和 top 都保证栈不为空

二、思路分析

好,看完题目的描述,我们来分析一下去求解这道题目

  • 我们知道,栈与队列的原理刚好相反,对于栈是【FILO】,对于队列是【FIFO】。这就需要我们灵活地去使用这两种数据结构进行解题。**对于本题,我的思路是这样的:**因为需要用队列来实现栈,首先其实可以想到的是使用两个队列,互相倒来倒去出队数据,图示如下

在这里插入图片描述

1、结构声明与展开剖析

  • 因为我使用的是C语言去解决这道题,所以无法使用STL中的queue来解决,于是就需要自己去写一个队列模拟【文末给出(链式队)】,这就会显得很麻烦,当然C++的代码我也会给出,我们主要讲授C语言的思维
  • 首先的话就是使用一个大的结构体定义两个内部队列
typedef struct {
    Qu q1;
    Qu q2;
} MyStack;
  • 因为我们使用时队列去模拟的栈,因此主接口还是对于栈的创建,因此需要为其开辟出一块空间来存储这两个队列,然后的话就是使用我们自己写的队列对这两个队列进行一个初始化。可以看到,对于两个队列,我没有定义成指针类型,不然的话对它们也要去开辟空间
  • 此时只需要传入定义的这两个队列的地址即可
MyStack* myStackCreate() {
    MyStack* myStack = (MyStack *)malloc(sizeof(MyStack));
    QueueInit(&(myStack->q1));
    QueueInit(&(myStack->q2));

    return myStack;
}

可能还是有同学对这个结构不太能想象地出来,这里给出它的结构图

  • 可以看到,这其实是一个三层嵌套的结构体,外层是题目给出的【MyStack】,然后是内层我们自己定义的两个链式队【q1】【q2】,而这两个链式队呢,又指向了一个单链表,所以对于这个结构来说还是比较复杂的,大家要理清这个结构

在这里插入图片描述


  • 说完了整体结构的思想,接下去我们主要来讲讲出队和入队这两个操作。它们的思想都是围绕于一个空、一个非空来进行

2、入栈【入队思想】

  • 入队其实很简单:也就是当哪个栈非空时,就往它那里入数据
//只往非空的队列中入数据
if(!QueueEmpty(&obj->q1))
    QueuePush(&obj->q1,x);
else
    QueuePush(&obj->q2,x);

3、出栈【出队思想】

  • 对于出队来说比较复杂,我们在出队前需要提前去推算出哪个队列是空的,然后我的思路将队列的前【n - 1】个元素进行出队,然后将它们入队到另外的一个空的队列中,最后获取原先的队列中还剩下的一个元素,接着将其出队,这样就实现了栈的先进先出特性
  • 然后我们来看看代码,首先的话就是去寻找出空和非空的两个队列,因为当后台程序调用到这个接口的时候就会传入这个【MyStack】,接着只需要去获取我们所定义的两个队列即可。
  • 这里解释一下为什么可以这么去获取,其实应该写成这样【&(obj->q1)】,只是因为【->】的优先级高于【&】,所以加不加都是可以的;使用这个MyStack所定义的指针去取到q1,接着传入q1的地址给到指针进行一个接收,使得等式两遍等价即可
Qu* EmptyQu = &obj->q1;
Qu* nonEmptyQu = &obj->q2;
if(!QueueEmpty(&obj->q1))
{       //必定是一个空,一个非空
    EmptyQu = &obj->q2;
    nonEmptyQu = &obj->q1;
}
  • 判断出谁为空,谁不空之后,我们就可以去进行一个出队入队的操作了,最后出得只剩下一个数据即可
//首先将非空队列中的前n-1个元素都出队
while(QueueSize(nonEmptyQu) > 1)
{
    QueuePush(EmptyQu,QueueFront(nonEmptyQu));  //每次取出非空队列的头元素
    QueuePop(nonEmptyQu);      //出队队头元素
}
  • 然后获取到这个数据返回,再将这个数据再出队即可,就达成了我们的目的
//此时非空队列中还剩一个元素,取出return即可
int front = QueueFront(nonEmptyQu);
QueuePop(nonEmptyQu);       //然后将此元素出队

return front;

4、获取栈顶元素【队列末尾】

  • 因为我们使用的是队列来模拟栈,因此队列的最后一个元素即为栈的栈顶元素,此时就可以回忆到我们在实现队列的时候写了一个Back()的接口
  • 此时只需要判断一下哪个队列不为空,取出这个队列的末尾元素返回即可
//返回非空队列的末尾元素
if(!QueueEmpty(&obj->q1))
    return QueueBack(&obj->q1);
else
    return QueueBack(&obj->q2);

5、逐步算法图解

  • 我们再通过步步的算法图解来分析一下,加深对代码的理解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 出队完成后继续入队,要找非空的队列入队

在这里插入图片描述

  • 好,我们继续执行一次出队操作
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 回忆一下,第一次的数据是【1237】,说明7是最后一个入队的,于是实现了第一个出队;接着有入队一个【4】,也是先出了这个元素,这就实现了先进先出的原则

三、整体代码展示

💻C语言代码实现

  • 题目代码使用的是C语言实现
typedef int QDataType;
typedef struct QueueNode {
	QDataType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue {
	QNode* front;
	QNode* rear;
	size_t sz;
}Qu;


/*初始化队列*/
void QueueInit(Qu* q);
/*销毁队列*/
void QueueDestroy(Qu* q);
/*入队*/
void QueuePush(Qu* q, QDataType x);
/*获取队头*/
QDataType QueueFront(Qu* q);
/*获取队尾*/
QDataType QueueBack(Qu* q);
/*出队*/
void QueuePop(Qu* q);
/*判空*/
bool QueueEmpty(Qu* q);
/*求解队列大小*/
size_t QueueSize(Qu* q);

//---------------------------------

typedef struct {
    Qu q1;
    Qu q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* obj = (MyStack *)malloc(sizeof(MyStack));
    QueueInit(&(obj->q1));
    QueueInit(&(obj->q2));

    return obj;
}

void myStackPush(MyStack* obj, int x) {
    //只往非空的队列中入数据
    if(!QueueEmpty(&obj->q1))
        QueuePush(&obj->q1,x);
    else
        QueuePush(&obj->q2,x);
}

int myStackPop(MyStack* obj) {
    Qu* EmptyQu = &obj->q1;
    Qu* nonEmptyQu = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {       //必定是一个空,一个非空
        EmptyQu = &obj->q2;
        nonEmptyQu = &obj->q1;
    }

    //首先将非空队列中的前n-1个元素都出队
    while(QueueSize(nonEmptyQu) > 1)
    {
        QueuePush(EmptyQu,QueueFront(nonEmptyQu));  //每次取出非空队列的头元素
        QueuePop(nonEmptyQu);      //出队队头元素
    }
    //此时非空队列中还剩一个元素,取出return即可
    int front = QueueFront(nonEmptyQu);
    QueuePop(nonEmptyQu);       //然后将此元素出队

    return front;
}

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) {
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);

    free(obj);
}

//---------------------------------
/*初始化队列*/
void QueueInit(Qu* q)
{
	q->front = NULL;
	q->rear = NULL;
	q->sz = 0;
}

/*销毁队列*/
void QueueDestroy(Qu* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		QNode* del = cur;
		cur = cur->next;
		free(del);		
		//del = NULL;		无需再将del置为空,因为其为局部变量不会被访问到
	}
	q->front = q->rear = NULL;		//头尾指针要置空
}

/*入队*/
void QueuePush(Qu* q, QDataType x)
{
	assert(q);
	/*创建结点初始化*/
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("fail malloc");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;

	/*尾插*/
	if (q->rear == NULL)
	{		//队列为空
		q->front = q->rear = newNode;
	}
	else
	{
		q->rear->next = newNode;
		q->rear = newNode;
	}
	q->sz++;		//结点个数 + 1
}

/*出队*/
void QueuePop(Qu* q)
{
	assert(q);
	assert(!QueueEmpty(q));

	//1.只有一个结点
	if (q->front == q->rear)
	{
		free(q->front);
		q->front = q->rear = NULL;
	}
	//2.有多个结点
	else
	{
		QNode* del = q->front;
		q->front = q->front->next;
		free(del);
	}
	q->sz--;
}
/*获取队头*/
QDataType QueueFront(Qu* q)
{
	assert(q);
	assert(!QueueEmpty(q));

	return q->front->data;
}

/*获取队尾*/
QDataType QueueBack(Qu* q)
{
	assert(q);
	assert(!QueueEmpty(q));

	return q->rear->data;
}

/*判空*/
bool QueueEmpty(Qu* q)
{
	assert(q);
	return q->front == NULL && q->rear == NULL;
}

/*求解队列大小*/
size_t QueueSize(Qu* q)
{
	return q->sz;
}


/**
 * 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);
*/

💻C++代码实现

  • 这里也给出C++的代码实现
  • C++的思路有所不同,无需去判断哪个队列为空,每次就第q1出队列,q2则作为暂时存放的队列,
class MyStack {
public:
    queue<int> q1;
    queue<int> q2;
    MyStack() {

    }
    
    void push(int x) {
        q1.push(x);
    }
    
    int pop() {
        int sz = q1.size();
        sz--;       //先让总长度减1,少出队一个元素
        while(sz--)
        {           //将sz - 1个元素先放入q2中暂时保存
            q2.push(q1.front());    
            q1.pop();
        }
        int ret = q1.front();      //此时q1中只剩一个元素,出队即为FILO
        q1.pop();

        //重置q1
        q1 = q2;
        while(!q2.empty())
        {       //清空q2
            q2.pop();
        }
        return ret;     //放在最后返回是因为在获取q1队首元素后要pop()掉,否则队列中会有剩余元素
    }
    
    int top() {
        return q1.back();
    }
    
    bool empty() {
        return q1.empty();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

【⭐】补充:单队列实现栈

  • 后来有想到一种方法,仅仅使用一个队列就可以实现
  • 整体思想是话其实差不多,只是用一个栈,首先一样也要去得出当前队列的元素个数,然后将出队的元素又重新加入到当前队列的末尾,知道剩下一个元素为止,就会需要出队的数据,一样可以实现使用队列去模拟栈
class MyStack {
public:
    queue<int> qu;
    MyStack() {

    }
    
    void push(int x) {
        qu.push(x);
    }
    
    int pop() {
        int sz = qu.size();
        sz--;
        while(sz--)
        {
            //将队头元素置为队尾元素,执行sz - 1此
            qu.push(qu.front());
            qu.pop();
        }
        //此时的出队顺序即为出栈的顺序
        int ret = qu.front();
        qu.pop();
        return ret;     //此处return 是因为最后执行时需要先将队列中元素清除再返回,否则队列不为空
    }
    
    int top() {
        return qu.back();
    }
    
    bool empty() {
        return qu.empty();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

四、总结与提炼

  • 最后我们来总结一下本文所介绍的内容,本文讲解的是一道力扣中有关栈与队列相关的题目,是使用队列来实现栈,在题目的分析过程中,我们使用到了两个队列去实现,通过去判断哪个队列是空还是非空,去进行一个入队和出队的操作,继而来实现一个出栈的顺序。在文末还给出了单个队列实现的方法,作为补充了解

以上就是本文所要描述的所有内容,感谢您对本文的观看,如有疑问请于评论区留言或者私信我都可以🍀

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

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

相关文章

谈谈用户态 TCP 协议实现

导语 TCP 协议是目前名气最大、使用最广泛的传输层网络协议。 TCP 是一个可靠的&#xff08;reliable&#xff09;、面向连接的&#xff08;connection-oriented&#xff09;、基于字节流&#xff08;byte-stream&#xff09;、全双工的&#xff08;full-duplex&#xff09;协…

低代码如何增强团队应用开发能力?

虽然数字化转型长期以来一直是企业的战略目标&#xff0c;但疫情加速了这一进程。企业为确保参与度和业务连续性而推出的第一个也是最直接的数字渠道之一是移动应用程序。 然而&#xff0c;对于大多数组织而言&#xff0c;最大的障碍之一是技术团队没有足够的带宽来创建具有严…

基于springboot+java+vue的健身房课程预约信息网站-计算机毕业设计

运行环境 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven 项目介绍 本健身网…

从零学习 InfiniBand-network架构(八) —— IB协议中的原子操作

从零学习 InfiniBand-network架构&#xff08;八&#xff09; —— IB协议中的原子操作 &#x1f508;声明&#xff1a; &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN主页 &#x1f511;未经作者允许&#xff0c;禁止转载 &#x1f6a9;本专题部分内容源于《InfiniBand-net…

Spring入门

一、Spring简介 1、Spring概述 官网地址&#xff1a;https://spring.io/ Spring 是最受欢迎的企业级 Java 应用程序开发框架&#xff0c;数以百万的来自世界各地的开发人员使用Spring 框架来创建性能好、易于测试、可重用的代码。 Spring 框架是一个开源的 Java 平台&#xf…

STM32F407输入捕获应用--PWM 输入模式测量脉冲频率与宽度

STM32F407输入捕获应用--PWM 输入模式测量脉冲频率与宽度一、测量脉宽或者频率二、PWM 输入模式三、软件实现3.1、硬件准备3.2代码3.4 验证输入捕获一般应用在两个方面&#xff0c;一个方面是脉冲跳变沿时间测量&#xff0c;另一方面是 PWM 输入测量。一、测量脉宽或者频率 1.测…

Dockerfile(容器与镜像 自定义nginx tomcat 镜像优化)

Dockerfile精讲一、容器与容器镜像之间的关系二、容器镜像分类三、容器镜像获取的方法四、容器镜像获取方法演示4.1 在DockerHub直接下载4.2 把操作系统中文件系统打包为容器镜像4.2.1 安装一个最化的操作系统4.2.2 把操作系统中文件系统进行打包4.2.3 把打包后文件加载至本地文…

连续信号与系统的频域分析之傅里叶级数

信号的傅里叶级数一、正交函数集二、三角形式傅里叶级数三、指数形式的傅里叶级数1. 定义2. 系数确定3. 典型信号的傅里叶级数4. 周期信号的功率谱在LTI系统的时域分析中&#xff0c;输入信号被分解成冲激信号或阶跃信号的叠加&#xff08;积分&#xff09;&#xff0c;然后利用…

LeetCode刷题2:链表篇

提示&#xff1a;本篇共7道力扣题目供大家食用&#xff0c;时间自行把控~ 算法刷题系列笔记 LeetCode刷题1&#xff1a;数组篇 文章目录算法刷题系列笔记作者有话说一、链表知识1.1 什么是链表?1.2 链表的类型1.3 链表操作二、经典题目2.1 Leetcode203.移除链表元素2.2 Leet…

vue-jest vue3

1. 使用vite搭建测试项目&#xff1a; vue create test-vue-jest 选择单元测试&#xff1a;Unit Testing--->jest 2. 配置 jest.config.js module.exports {transform: {"^.\\.vue$": "vue-jest",},preset: vue/cli-plugin-unit-jest/presets/types…

【关于几个问题的思考】

项目如何部署在服务器上面的 此文章以springboot 项目为例&#xff1a; 1.需要申请一台服务器&#xff0c;安装Linux操作系统 2.在服务器安装jdk ,不需要安装Tomcat&#xff0c;springboot 项目内嵌 Tomcat&#xff1b;安装项目的依赖项&#xff1a;如mysql redis等 3.此处利用…

Oracle SQL执行计划操作(9)——位图相关操作

​​​​​​9. 位图相关操作 该类操作基于位图数据(例如:位图索引的位图数据)进行位操作,或将其他结构数据转成位图数据以利用位操作。根据不同的具体SQL语句及其他相关因素,如下各操作可能会出现于相关SQL语句的执行计划。 1)BITMAP AND 对两个位图索引进行“与”(…

【CLS数据淘金第四期】网络流日志-云联网日志分析

导语 云联网&#xff08;Cloud Connect Network&#xff0c;CCN&#xff09;云联网覆盖全球 20 地域&#xff0c;提供云上私有网络间&#xff08;VPC&#xff09;、VPC 与本地数据中心间&#xff08;IDC&#xff09;内网互联的服务&#xff0c;具备全网多点互联、路由自学习、…

Hypermesh三维网格划分技能,以汽车发动机连杆结构为例

作者&#xff1a;米条老师&#xff0c;仿真秀专栏作者 Hypermesh三维单元的划分功能还是非常的丰富和灵活的&#xff0c;通常掌握主要的几个命令基本就能够满足大部分的网格划分工作。首先我们先了解一下在hypermesh中实现三维网格划分的基本步骤&#xff1a; 几何部件分组及…

finereport开发者需要关注的问题

版本问题 如果使用高版本开发&#xff0c;则无法在低版本的设计使用与发布, 所以第一件事情就是要把设计器的版本弄好。 请使用v10.0.0(本地版本需要与当前的中台版本一致)&#xff0c;目前官方已没有直接下载到v10.0.0完整包的地址了&#xff0c;例如&#xff1a;下图为当前的…

大佬指明方向!使用微服务的最佳实践以及如何避免采用微服务架构可能带来的复杂性陷阱

什么是微服务 微服务应用是与单体应用区分开来的. 当一个单体项目随着业务的发展会越来越膨胀,变得更加难维护,从一开始仅需一两个人到需要两三个团队,多个团队维护同一个项目无疑是一场灾难,沟通成本大大增加,技术协同也会十分困难. 举个例子,我在上一家公司维护过一个后台项…

IP地址虚拟网络与虚拟机网络配置

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 IP地址虚拟网络与虚拟机网络配置1. IP地址与子网划分☞IP地址☞特殊IP地址☞IP地址申请☞子网掩码☞子…

spider

大家好 我是Yhen 今天给大家分享一下 如何爬取卡塔尔世界杯球员榜 数据来源&#xff1a;百度体育 https://tiyu.baidu.com/match/%E4%B8%96%E7%95%8C%E6%9D%AF/tab/%E7%90%83%E5%91%98%E6%A6%9C/current/0 文章目录一.需求分析二.思路分析三.代码实战四.源码获取一.需求分析…

强强联合 加速科技“牵手”清华大学达成深度战略合作

近日&#xff0c;清华大学集成电路学院成功采购了加速科技ST2500系列高性能数模混合测试机&#xff08;浙江省首台套装备&#xff09;。加速科技“牵手”清华大学&#xff0c;共同推进集成电路自主技术的创新升级&#xff0c;就集成电路学科教学科研、人才培养展开深度合作。 清…

Java Spring后处理器

Java Spring后处理器 在Spring框架中&#xff0c;交给Spring管理的类信息都会被Spring底层自动加载被封装成对应的BeanDefinition对象存储在beanDefinitionMap的Map集合中去&#xff0c;那么除了直接将类信息配置的方式外&#xff0c;还有别的方式可以对想要交给Spring管理的类…