用队列实现栈(图示超详解哦)

全文目录

  • 引言
  • 用队列实现栈
    • 题目介绍
    • 思路简述
    • 实现
      • 队列的部分
      • 栈的部分
        • 栈的创建
        • 判断栈是否为空
        • 压栈
        • 出栈
        • 访问栈顶元素
        • 栈的释放
  • 总结

引言

在了解了栈和队列的知识后,我们已经对它们的特点有了一定的了解:栈是先进后出,队列是先进先出:
戳我康栈详解哦
戳我康队列详解哦

接下来的两篇文章就来介绍用两个队列实现栈以及用两个栈实现队列的OJ练习:
用队列实现栈OJ链接

用队列实现栈

题目介绍

在这里插入图片描述
题目描述很简单:
通过两个队列实现栈存储数据的特点,即先进后出:

我们需要实现push、pop、top、empty、free:
void push(int x) 将元素 x 压入栈顶;
int pop() 移除并返回栈顶元素;
int top() 返回栈顶元素;
bool empty() 如果栈是空的,返回 true ;否则,返回 false 。

思路简述

这道题的难点主要在于如何pop队列中后存储的元素,因为队列的特点是先进先出,所以在队列尾的元素是无法直接移除的。
我们很容易的想到利用第二个队列,将队列中的元素转移到另一队列中,只留一个元素在原队列中,这个剩余的元素就是最后入的元素,将这个元素移除即可;
如果这时要再移除尾的元素,就将队列中的元素再移回原来的队列,删除剩下的一个元素即可;
如果此时要再插入元素,就直接在有元素的那个队列尾插入即可,再移除时再倒一遍即可:

因为,如果从一个队列中将元素从队列头依次移出,再依次插入到另一个队列中时,数据的顺序是不改变的。所以如果要再尾插入元素时直接在有元素的那个队列插入即可;尾删时,只需要重复的倒数据即可:

在这里插入图片描述

实现

队列的部分

在用两个队列实现栈之前,我们需要实现队列的接口,在用其实现栈时直接复用即可。关于队列的实现这里就不再赘述了,在前面的文章中已经详细的介绍过了。这里直接把前面实现过的队列的各种接口拷贝过来,包括用于定义队列的结构体:

typedef int QDataType;

typedef struct QListNode//队列的每个结点
{
	struct QListNode* pNext;
	QDataType data;
}QNode;

typedef struct Queue// 队列的结构
{
	QNode* front;
	QNode* rear;
	int size;
}Queue;

// 队列的部分
// 初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->front = NULL;
	pq->rear = NULL;
	pq->size = 0;
}
// 检测队列是否为空,如果为空返回非0,非空返回0
int QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->front == NULL;
}
// 队尾入队列
void QueuePush(Queue* pq, QDataType data)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	assert(newnode);
	newnode->data = data;
	newnode->pNext = NULL;
	if (QueueEmpty(pq))
	{
		pq->front = newnode;
		pq->rear = newnode;
	}
	else
	{
		pq->rear->pNext = newnode;
		pq->rear = newnode;
	}
	pq->size++;
}
// 队头出队列
void QueuePop(Queue* pq)
{
	assert(pq && QueueEmpty(pq)==0);
	QNode* cur = pq->front->pNext;
	if (cur)
	{
		free(pq->front);
		pq->front = cur;
	}
	else
	{
		free(pq->front);
		pq->front = NULL;
		pq->rear = NULL;
	}
	pq->size--;
}
// 获取队列头部元素
QDataType QueueFront(Queue* pq)
{
	assert(pq && QueueEmpty(pq)==0);
	return pq->front->data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq && QueueEmpty(pq)==0);
	return pq->rear->data;
}
// 销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->front;
	QNode* next = pq->front;
	while (cur)
	{
		next = cur->pNext;
		free(cur);
		cur = next;
	}
	pq->front = NULL;
	pq->rear = NULL;
	pq->size = 0;
}

栈的部分

有了队列的实现后,我们需要创建两个队列q1与q2,并将这两个队列存放在一个结构体变量中方便访问:

typedef struct //两个队列
{
	Queue q1;
	Queue q2;
} MyStack;

栈的创建

对于这个接口,题目并没有给出参数。我们就直接为结构体MyStack动态开辟一块空间,并且初始化即可。
初始化时,我们可以直接复用队列初始化函数QueueInit,初始化两个队列即可:

//创建栈
MyStack* myStackCreate() 
{
	MyStack* pmst = (MyStack*)malloc(sizeof(MyStack));
	assert(pmst);
	QueueInit(&pmst->q1);
	QueueInit(&pmst->q2);
	return pmst;
}

判断栈是否为空

栈为空,即两个队列均为空。
所以我们直接复用判断队列是否为空函数QueueEmpty,当两个队列均为空,即两个函数的返回值均为真时,返回真,否则返回假:

//判断栈是否为空为空返回1,非空返回0
bool myStackEmpty(MyStack* obj)
{
	assert(obj);
	if (QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2))//当两个队列全为空时才为空
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

压栈

压栈时,我们需要将元素插入到不为空的那个队列中,若均为空,就随便插入一个:
在这里插入图片描述
插入时,我们就直接复用插入队列函数QueuePush即可:

//从栈顶入栈
void myStackPush(MyStack* obj, int x) 
{
	assert(obj);
	if (QueueEmpty(&obj->q1)==0)//在有数据的那个队列入栈
	{
        assert(QueueEmpty(&obj->q2));
		QueuePush(&obj->q1, x);
	}
	else
	{
        assert(QueueEmpty(&obj->q1));
		QueuePush(&obj->q2, x);
	}
}

出栈

出栈时,我们需要将有元素的那个队列中的元素转移到空的队列中,直到剩一个元素。将这个元素移除即可:
在这里插入图片描述
在转移元素时,我们可以复用 QueueFront函数,取出队列头的元素,再用QueuePush插入到另一个队列的尾。然后再QueuePop,删除原队列中的首元素即可:

//从栈顶出栈
int myStackPop(MyStack* obj) 
{
	assert(obj && myStackEmpty(obj)==0);
	QDataType ret = 0;
	//将有数据的那个队列转移到空队列中,返回剩下的那个数据
	if(QueueEmpty(&obj->q1)==0 && QueueEmpty(&obj->q2))
	{
		while (obj->q1.size > 1)
		{
			QueuePush(&obj->q2, QueueFront(&obj->q1));
			QueuePop(&obj->q1);
		}
		ret = QueueFront(&obj->q1);
		QueuePop(&obj->q1);
	}
	else if (QueueEmpty(&obj->q2)==0 && QueueEmpty(&obj->q1))
	{
		while (obj->q2.size > 1)
		{
			QueuePush(&obj->q1, QueueFront(&obj->q2));
			QueuePop(&obj->q2);
		}
		ret = QueueFront(&obj->q2);
		QueuePop(&obj->q2);
	}
	else
	{
		return EOF;
	}
	return ret;
}

访问栈顶元素

访问栈顶元素时,即访问有元素的那个队列的尾元素即可。
我们可以直接复用访问队列尾元素的 QueueBack函数:

//访问栈顶元素
int myStackTop(MyStack* obj) 
{
	assert(obj && myStackEmpty(obj) == 0);
	QDataType ret = 0;
	if (QueueEmpty(&obj->q1) == 0 && QueueEmpty(&obj->q2))//访问有数据的那个队列的尾数据
	{
		ret = QueueBack(&obj->q1);
	}
	else if (QueueEmpty(&obj->q2) == 0 && QueueEmpty(&obj->q1))
	{
		ret = QueueBack(&obj->q2);
	}
	else
	{
		return EOF;
	}
	return ret;
}

栈的释放

释放栈时,直接复用队列释放函数QueueDestroy,分别释放两个队列。最后再释放动态开辟的结构体变量即可:

//释放栈
void myStackFree(MyStack* obj) 
{
	QueueDestroy(&obj->q1);
	QueueDestroy(&obj->q2);
	free(obj);
}

总结

到此,关于用两个队列实现栈的题目就介绍完了,在下一篇文章中将介绍用两个栈实现队列,欢迎大家持续关注哦

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GPT-4发布,这类人才告急,大厂月薪10W+疯抢

ChatGPT最近彻底火出圈,各行各业都在争相报道,甚至连很多官媒都下场“跟风”。ChatGPT的瓜还没吃完,平地一声雷,GPT-4又重磅发布! 很多小伙伴瑟瑟发抖:“AI会不会跟自己抢饭碗啊?” 关于“如何…

ChatGPT新进展GPT-4 模型介绍

文章目录背景工具功能使用增强背景 2023.3.14 GPT-4 模型发布 创建了GPT-4,这是OpenAI在扩大深度学习方面的最新里程碑。GPT-4是一个大型多模态模型(接受图像和文本输入,输出文本输出),虽然在许多现实场景中不如人类,但在各种专业…

【数据结构与算法】 - 线性表详解 - (带头结点)单链表详细实现思路及代码

目录 一、概述 二、线性表介绍 三、单链表的操作实现  📌3.1 C语言定义链表结点  📌3.2 单链表初始化  📌3.3 单链表插入数据  📌3.4 单链表删除数据  📌3.5 单链表查找数据  📌3.6 单链表的销毁 四、…

基于51单片机的自动打铃打鸣作息报时系统AT89C51数码管三极管时钟电路

wx供重浩:创享日记 对话框发送:单片机打铃 获取完整无水印论文报告说明(含源码程序、电路原理图和仿真图) 本次设计中的LED数码管电子时钟电路采用24小时制记时方式,本次设计采用AT89C51单片机的扩展芯片和6个PNP三极管做驱动&…

C/C++每日一练(20230325)

目录 1. 搜索插入位置 🌟 2. 结合两个字符串 🌟 3. 同构字符串 🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 搜索插入位置 给定一个排序数…

【事故】记一次意外把公司项目放到GitHub并被fork,如何使用DMCA下架政策保障隐私

前言 🍊缘由 在一个月黑风高的夜晚,正准备休息的我突然接到之前外包老总的亲切问候。一顿输出才知道三年前为了搭建流程化部署,将公司的测试代码放到github上后忘记删除。现在被甲方的代码扫描机制扫到,并且检查到代码已经被其他…

Oracle-CDC进程同步报错问题合集

前言: Oracle CDC是数据库自带的数据库数据复制和增量数据抽取工具,提供五种复制模式 1 Synchronous Change Data Capture Configuration(同步复制) 2 Asynchronous HotLog Configuration(异步在线日志CDC) 3 Asynchronous Distributed HotLog Configuratio…

Android开发工程师想找工作需要掌握哪些

前言 目前互联网行业越来越好,进入这个行业的人员也是越来越多。从开发的角度来看,开发的职位主要分前端,后端,客户端(主要分为ios和android开发)以及算法工程师等。 Android开发一直是当前互联网行业中最…

快速排序,分治法实际应用(含码源与解析)

🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨,经典算法的解析✨都在这儿,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -…

微服务中的分布式事务管理 - 2/2 Saga异步模式

转载请注明来源:https://janrs.com/h42y 这篇文章是上一篇文章的延续。 在这篇文章中,我们将看到Saga模式,它是一种异步模式,在每个微服务中执行一连串的事务,并发布消息或事件以进行下一步。如果中间有任何步骤失败&…

吉利汽车智能驾驶掌舵人胡金龙离职!NOA「换道超车」被按下暂停键?

2022年是中国自主品牌全面实现智能驾驶「换道超车」的关键一年。 高工智能汽车研究院研究院监测数据显示,2022年自主品牌(不含合资车型)完成年度总交付909.68万辆,同比增长6.39%,逆势跑赢市场。比亚迪、上汽、吉利、长…

Web自动化测试(二)(全网最给力自动化教程)

欢迎您来阅读和练手!您将会从本章的详细讲解中,获取很大的收获!开始学习吧! 2.4 CSS定位2.5 SeleniumBuilder辅助定位元素2.6 操作元素(键盘和鼠标事件) 正文 2.4 CSS定位 前言 大部分人在使用selenium定…

【字体图标iconfont】字体图标部署流程+项目源码分析

今日,心情甚是烦闷,原由… 公司项目需要将字体图标做一些细微的调整,我一人分析了许久,看不大懂源码的逻辑,产生了自我怀疑。深吸一口气,重新鼓起勇气,调整心境,一下子豁然开朗&…

【sentinel】熔断降级规则详解及源码分析

概述 除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方API等。例如,支付的时候,可能需要远程调用银联…

ChatGPT使用介绍、ChatGPT+编程、相关组件和插件记录

文章目录介绍认识ChatGPT是通过英汉互译来实现中文回答的吗同一个问题,为什么中英文回答不同ChatGPT的使用对话组OpenAI APIAI智能绘图DALLE 2ChatGPT for Google插件ChatGPT编程编写代码代码错误修正与功能解读代码评审与优化推荐技术方案编写和优化SQL语句在代码编…

Linux操作系统ARM指令集与汇编语言程序设计

一、实验目的1.了解并掌握ARM汇编指令集2.应用ARM指令集编写一个程序操控开发板上的LED灯二、实验要求应用ARM汇编指令集编写程序,实现正常状态下开发板上的LED灯不亮,按下一个按键之后开发板上的LED灯进入流水灯模式。三、实验原理四个LED灯的电路如下图…

第二十二天 数据库开发-MySQL(DQL、多表设计)

目录 数据库开发-MySQL 1. 数据库操作-DQL 1.1 介绍 1.2 语法 1.3 基本查询 1.4 条件查询 1.5 排序查询 1.6 分页查询 1.7 聚合函数 1.8 分组查询 2. 多表设计 2.1 一对多 2.2 一对一 2.3 多对多 2.4 案例 数据库开发-MySQL 1. 数据库操作-DQL 1.1 介绍 DQL英文…

免费镜像 ChatGPT 网站随你挑和分享一批可用的 API Keys

文章目录一、前言二、在线 ChatGPT三、分享一批 API Keys🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、前言 随着科技的不断进步,人工智能在各个领域的应用越来越广泛。在这个过程中,人们需要不断更新知识和技能&#x…

SpringBoot整合数据可视化大屏使用

1 前言 DataV数据可视化是使用可视化应用的方式来分析并展示庞杂数据的产品。DataV旨让更多的人看到数据可视化的魅力,帮助非专业的工程师通过图形化的界面轻松搭建专业水准的可视化应用,满足您会议展览、业务监控、风险预警、地理信息分析等多种业务的展示需求, 访问地址:h…

第一部分——简单句——第一章——简单句的核心——第二节 简单句的核心变化——主语,表语,宾语的变化

之前我们学了谓语动词的五种变化(三态加一否) 主语,宾语,表语都是围绕着谓语动词的对象。 (1)主语可以是名词/代词 (2)或者是相当于名词的(非谓语动词doing&#xff0c…
最新文章