栈与队列的对决:如何用栈实现队列?

news2024/11/27 16:27:37

在这里插入图片描述

本篇博客会讲解力扣“232. 用栈实现队列”的解题思路,这是题目链接。

先来审题:
在这里插入图片描述
以下是输出示例:
在这里插入图片描述
以下是提示和进阶:
在这里插入图片描述
栈是一种后进先出的数据结构,而队列是一种先进先出的数据结构,如何用栈实现队列呢?

整体的思路是,使用2个栈,分别是pushst和popst。考虑模拟实现队列后,入队列和出队列分别应该如何操作:

  1. 入队列:直接把数据插入到pushst中。
  2. 出队列:若popst为空,则把pushst中的所有的数据都转移到popst中,popst再出栈;若popst非空,则直接popst出栈。

稍微模拟以下:先入[1 2 3 4 5]。
在这里插入图片描述
接着出队列。此时应该出1,但是1在pushst的最底下怎么办?我们的思路是:把数据转移过去。每次取pushst栈顶的元素,插入到popst中,pushst再出栈,直到pushst为空。先转移5:
在这里插入图片描述

转移4:
在这里插入图片描述
以此类推,把所有数据都转移过去:
在这里插入图片描述
1就跑到popst的最顶上了,此时删除1就是出队列。

在这里插入图片描述
如果还要继续出队列呢?popst继续出栈即可,2就被删除了。
在这里插入图片描述
接着,假设[6 7 8]入队列,插入到pushst中:
在这里插入图片描述
接着出队列,由于popst不为空,直接popst出栈,把3删了:
在这里插入图片描述
若继续出队列2次,就popst出栈2次,4、5就被删除了:
在这里插入图片描述
此时若继续出队列,由于popst已经为空,就需要把pushst中的数据全部转移到popst中,重复上面的步骤就行了。

下面开始实现代码:

先实现一个栈,由于本题的重点不是栈的实现,这里直接上代码。关于如何使用C语言实现栈,我会在另外一篇博客中单独讲解。

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;      // 栈顶
	int capacity; // 容量
}Stack;

// 初始化栈
void StackInit(Stack* ps);

// 入栈
void StackPush(Stack* ps, STDataType data);

// 出栈
void StackPop(Stack* ps);

// 获取栈顶元素
STDataType StackTop(Stack* ps);

// 获取栈中有效元素个数
int StackSize(Stack* ps);

// 检测栈是否为空
bool StackEmpty(Stack* ps);

// 销毁栈
void StackDestroy(Stack* ps);

void StackInit(Stack* ps)
{
	assert(ps);

	// 开辟4个空间
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	// 初始化
	ps->top = 0;
	ps->capacity = 4;
}

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);

	// 检查容量
	if (ps->top == ps->capacity)
	{
		// 扩容2倍
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		// 扩容成功
		ps->a = tmp;
		ps->capacity *= 2;
	}

	// 数据压栈
	ps->a[ps->top++] = data;
}

void StackPop(Stack* ps)
{
	assert(ps);
	// 检查非空
	assert(!StackEmpty(ps));

	ps->top--;
}

STDataType StackTop(Stack* ps)
{
	assert(ps);
	// 检查非空
	assert(!StackEmpty(ps));

	return ps->a[ps->top - 1];
}

int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

bool StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}

void StackDestroy(Stack* ps)
{
	assert(ps);

	// 释放空间
	free(ps->a);
	ps->top = 0;
	ps->capacity = 0;
}

下面实现MyQueue。结构的定义是2个栈:

typedef struct {
	Stack pushst;
	Stack popst;
} MyQueue;

初始化时,对2个栈分别初始化即可。

MyQueue* myQueueCreate() {
	MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
	if (obj == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	// 初始化2个栈
	StackInit(&obj->pushst);
	StackInit(&obj->popst);

	return obj;
}

入队列时,只需对pushst入栈。

void myQueuePush(MyQueue* obj, int x) {
	StackPush(&obj->pushst, x);
}

接下来实现peek函数,即取队头的数据。取法非常简单,若popst为空,就把pushst中的数据全部转移到popst中,否则直接取popst的栈顶元素。严谨起见,最好断言一下队列非空。

int myQueuePeek(MyQueue* obj) {
	assert(!myQueueEmpty(obj));

	if (StackEmpty(&obj->popst))
	{
		// popst为空,把pushst中的数据插入到popst中
		while (!StackEmpty(&obj->pushst))
		{
			// 转移数据
			StackPush(&obj->popst, StackTop(&obj->pushst));
			StackPop(&obj->pushst);
		}
	}

	return StackTop(&obj->popst);
}

根据前面的讲解,pop的思路就是peek后(此时popst非空,若空,pushst中的数据就会被全部转移到popst中),popst再出栈。严谨起见,记得断言队列非空。

int myQueuePop(MyQueue* obj) {
	assert(!myQueueEmpty(obj));

	int ret = myQueuePeek(obj);
	// 此时popst不为空
	StackPop(&obj->popst);
	return ret;
}

若2个栈都为空,则队列为空。

bool myQueueEmpty(MyQueue* obj) {
	return StackEmpty(&obj->pushst) && StackEmpty(&obj->popst);
}

销毁时,分别销毁2个栈,再销毁整体。

void myQueueFree(MyQueue* obj) {
	// 销毁2个栈
	StackDestroy(&obj->pushst);
	StackDestroy(&obj->popst);

	free(obj);
	obj = NULL;
}

以下是完整的代码:

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;      // 栈顶
	int capacity; // 容量
}Stack;

// 初始化栈
void StackInit(Stack* ps);

// 入栈
void StackPush(Stack* ps, STDataType data);

// 出栈
void StackPop(Stack* ps);

// 获取栈顶元素
STDataType StackTop(Stack* ps);

// 获取栈中有效元素个数
int StackSize(Stack* ps);

// 检测栈是否为空
bool StackEmpty(Stack* ps);

// 销毁栈
void StackDestroy(Stack* ps);

void StackInit(Stack* ps)
{
	assert(ps);

	// 开辟4个空间
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	// 初始化
	ps->top = 0;
	ps->capacity = 4;
}

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);

	// 检查容量
	if (ps->top == ps->capacity)
	{
		// 扩容2倍
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		// 扩容成功
		ps->a = tmp;
		ps->capacity *= 2;
	}

	// 数据压栈
	ps->a[ps->top++] = data;
}

void StackPop(Stack* ps)
{
	assert(ps);
	// 检查非空
	assert(!StackEmpty(ps));

	ps->top--;
}

STDataType StackTop(Stack* ps)
{
	assert(ps);
	// 检查非空
	assert(!StackEmpty(ps));

	return ps->a[ps->top - 1];
}

int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

bool StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}

void StackDestroy(Stack* ps)
{
	assert(ps);

	// 释放空间
	free(ps->a);
	ps->top = 0;
	ps->capacity = 0;
}

typedef struct {
	Stack pushst;
	Stack popst;
} MyQueue;

MyQueue* myQueueCreate() {
	MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
	if (obj == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	// 初始化2个栈
	StackInit(&obj->pushst);
	StackInit(&obj->popst);

	return obj;
}

void myQueuePush(MyQueue* obj, int x) {
	StackPush(&obj->pushst, x);
}

bool myQueueEmpty(MyQueue* obj);

int myQueuePop(MyQueue* obj) {
	assert(!myQueueEmpty(obj));

	int ret = myQueuePeek(obj);
	// 此时popst不为空
	StackPop(&obj->popst);
	return ret;
}

int myQueuePeek(MyQueue* obj) {
	assert(!myQueueEmpty(obj));

	if (StackEmpty(&obj->popst))
	{
		// popst为空,把pushst中的数据插入到popst中
		while (!StackEmpty(&obj->pushst))
		{
			// 转移数据
			StackPush(&obj->popst, StackTop(&obj->pushst));
			StackPop(&obj->pushst);
		}
	}

	return StackTop(&obj->popst);
}

bool myQueueEmpty(MyQueue* obj) {
	return StackEmpty(&obj->pushst) && StackEmpty(&obj->popst);
}

void myQueueFree(MyQueue* obj) {
	// 销毁2个栈
	StackDestroy(&obj->pushst);
	StackDestroy(&obj->popst);

	free(obj);
	obj = NULL;
}

在这里插入图片描述
通过喽。

总结

用栈实现队列的思路:

  1. 使用2个栈,分别是pushst和popst。
  2. 入队列:pushst入栈。
  3. 出队列:若popst未空,则先转移数据,保证popst中有数据。接着,popst出栈。

感谢大家的阅读!

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

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

相关文章

chatgpt赋能Python-pingouin_python

了解Pingouin Python对数据分析和统计学的优势 介绍Pingouin Python Pingouin Python是一个强大的Python包,它提供了在数据分析和统计学中所需的一些主要函数。使用Pingouin Python可以方便地进行t-检验、方差分析、相关性等常用的数据分析和统计学任务。 此外&a…

Vue组件复杂表格高级编辑功能

Vue 组件复杂表格高级编辑功能 文章目录 Vue 组件复杂表格高级编辑功能1. sync 父子组件数据同步更新2. 在 el-table 中开发高级编辑表格功能3. 参考文献 在vue中组件的定义是希望组件可以做单一的功能,做到高复用,低耦合,所以父子组件之间的…

【每日一题/简单模拟题】2446. 判断两个事件是否存在冲突

⭐️前面的话⭐️ 本篇文章介绍【2446. 判断两个事件是否存在冲突】题解,算法标签:【模拟】,【字符串】,展示语言c/java。 📒博客主页:未见花闻的博客主页 🎉欢迎关注🔎点赞&#x…

某大学信息安全竞赛——栈迁移加强版——只溢出0x8,无限ROP

芝士题目: 链接:https://pan.baidu.com/s/1uwFlcSg94MuC2tPi-HCb9w 提取码:joj6 感悟: 之前我只做过溢出超过0x10这样的栈迁移,思路就是找机会去泄露栈空间的地址然后把栈迁移到我们可以控制的栈空间,亦…

Linux_证书_Openssl工具详解

文章目录 OpenSSLopenssl实现对称加密openssl生成密钥对、非对称加密、数字签名根据CA颁布证书生成ca私钥和ca证书根据ca生成证书 小结 OpenSSL OpenSSL 是一个开源项目,其组成主要包括一下三个组件: openssl:多用途的命令行工具 libcrypt…

【滤波专题-第7篇】“类EMD”算法分解后要怎样使用(3)——EMD降噪方法及MATLAB代码实现

使用EMD分解(以及其他“类EMD”分解方法,以下为了简便统称EMD)做信号降噪,是EMD的一个比较重要的应用方向。EMD可以将复杂的信号分解为一系列的固有模态函数(IMFs),每一个IMF都包含了信号的一部…

“源擎”攻破银行核心系统建设痛点

银行业作为操作密集、数据密集、风险密集的行业,在向云转型的过程中面临着诸多独特的挑战,如银行需要具备不间断的业务创新能力,而不被系统开发周期制约;单一系统的开发和升级方式,越来越难以满足日益综合化的业务创新…

chatgpt赋能Python-pycharm关联python

Pycharm关联Python的介绍 Pycharm是一种非常流行的Python集成开发环境,开发人员可以在其中编写、调试和运行Python代码。Pycharm具有许多有用的功能,这些功能可以大大提高代码的效率和质量。其中一个最重要的功能是Pycharm如何关联Python,这…

HTB靶机012-Valentine-WP

012-Valentine 靶机IP:10.10.10.79 Scan nmap端口扫描: ┌──(xavier㉿kali)-[~] └─$ sudo nmap -sSV -T4 10.10.10.79 -F Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-29 00:47 CST Nmap scan report for 10.10.10.79 Host is up (0.30s…

chatgpt赋能Python-pycharm和python关联

PyCharm与Python:超越代码编写的完美结合 如果你是一位Python开发者,那么你肯定需要一个好用的开发环境,以便快速且高效地完成代码任务。而PyCharm就是这样一个优秀的Python开发IDE。它专注于提高Python开发速度和质量,让Python编…

Flowable 生成的表都是干嘛的?(一)

一.简介 Flowable 默认一共生成了 79 张数据表,了解这些数据表,有助于我们更好的理解 Flowable 中的各种 API。 接下来我们就对这 79 张表进行一个简单的分类整理。 ACT_APP_*(5)ACT_CMMN_*(12)ACT_CO_*…

chatgpt赋能Python-pycharm取消所有断点

Pycharm取消所有断点:提高编程效率的必备技巧 Pycharm作为Python程序员必备的开发工具之一,其强大的调试功能广受好评。但是,在开发过程中,我们可能会设置过多的断点或者设置了错误的断点,这样会让程序的运行速度变慢…

Java面向对象程序设计实验报告(实验四 抽象类的练习)

✨作者:命运之光 ✨专栏:Java面向对象程序设计实验报告 ​ 目录 ✨一、需求设计 ✨二、概要设计 ✨三、详细设计 ✨四、调试结果 ✨五、测试结果 ✨附录:源程序代码(带注释) demo4类 Car类 Circle类 Shape…

谷歌chrome浏览器无法自动播放video标签视频的问题

问题根源详见:Chrome中的自动播放政策>> https://developer.chrome.com/blog/autoplay/ The Autoplay Policy launched in Chrome 66 for audio and video elements and is effectively blocking roughly half of unwanted media autoplays in Chrome. For t…

基于数组实现的顺序表(SeqList)

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。 它的详细定义如下: 顺序表是一种数据结构,用于存储一组具有相同数据类型的元素,并按照元素在内存中的…

数据库【库,表操作】

目录 简单了解1.连接数据库服务器2.创建数据库3.创建表4.使用数据库5.数据库框架6.SQL的分类7.存储引擎 库操作1.创建数据库2.查看系统默认字符集以及校验规则3.查看数据库支持的字符集和校验规则4.查看数据库5.显示数据库语句6.删除数据库7.修改数据库8.备份和恢复9.查看连接情…

20 SQL——多表查询 (消除无效笛卡尔积)

create table dept(id int primary key auto_increment,name varchar(15))comment 部门;insert into dept(id, name) values (1,研发部),(2,市场部),(3,财务部),(4,销售部),(5,总经办),(6,人事部);create table staff (id int primary key auto_increment commentID,name …

【面试篇】Redis持久化面试题

文章目录 Redis持久化🙎‍♂️面试官:什么是Redis持久化? AOF日志AOF日志原理🙎‍♂️面试官:AOF日志是怎么工作的/AOF写入磁盘的流程?🙎‍♂️面试官: 刚刚说到了Redis先执行写入的…

Discourse Math 插件

概述Discourse Math 使用 MathJax (默认) 或者 KaTeX 来让你在你的 Discourse 中使用数学公式。 仓库链接GitHub - discourse/discourse-math: Official MathJax support for Discourse Install Guide如何在 Discourse 中安装插件 这个插件是 Discourse 官方提供的插件&#x…

javascript基础一:Javscript数组的常用方法有哪些?

在日常开发中,我们对数组可以说操作最多,这里我们来整理下数组的一下最常用的方法 数组基本操作可以归纳为 增、删、改、查,需要留意的是哪些方法会对原数组产生影响,哪些方法不会 下面对数组常用的操作方法做一个归纳 一、基本…