栈和队列OJ题:LeetCode--232.用栈实现队列

news2024/11/24 20:15:30

朋友们、伙计们,我们又见面了,今天给大家带来的是LeetCode--232.用栈实现队列

数 据 结 构 专 栏:数据结构

个    人   主    页 :stackY、

LeetCode 专  栏 :LeetCode刷题训练营

LeetCode--232.用栈实现队列:https://leetcode.cn/problems/implement-queue-using-stacks/

目录

1.题目介绍

2.实例演示

3.解题思路

3.1创建队列

3.2入列

3.3出列

3.4获取队头元素

3.5优化代码

3.6检测队列是否为空

3.7销毁队列 

4.完整代码


1.题目介绍

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

        1. void push(int x) 将元素 x 推到队列的末尾
        2. int pop() 从队列的开头移除并返回元素
        3. int peek() 返回队列开头的元素
        4. boolean empty() 如果队列为空,返回 true ;否则,返回 false
说明:

        你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。

你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

2.实例演示

3.解题思路

在做题之前我们先来进行简单的梳理逻辑,要通过两个栈来实现队列的功能,首先得清楚栈和队列都有什么区别,不熟悉的宝子可以去看看数据结构:栈和队列,队列的功能先进先出,而栈的功能是后进先出,那么有两个栈,要实现队列这种先进先出的功能就比较简单了,可以进行数据的转化,要将数据出队列,那么就需要将这个栈中的数据的前n-1个数据先插入到另外的一个栈,然后再将最后的元素出栈即可,这样就实现了队列的出列操作,那么在入列的时候我们是往哪个栈中入数据呢?所以我们的两个栈分别要有他们的功能,一个用来出数据,另外一个用来入数据,这样子就比较方便了。

3.1创建队列

使用两个栈来实现队列的功能,首先我们得写出一个栈,队列的功能是先进先出,栈的功能是后进先出,在创建队列的时候需要有两个栈,然后为队列创建一块空间,并且将两个栈都进行初始化:

实现栈:

//动态栈
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;        //栈顶
	int capacity;  //栈的容量
}Stack;


//对栈的初始化
void StackInit(Stack* pst);

//入栈
void StackPush(Stack* pst, STDataType x);

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

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

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

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

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

//对栈的初始化
void StackInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	//top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
	//pst->top = -1     
	//top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
	pst->top = 0;
	pst->capacity = 0;
}

//入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	//检测容量
	if (pst->top == pst->capacity)
	{
		int NewCapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		//当pst->a为NULL时执行的功能是和malloc一样
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = NewCapacity;
	}
	//入栈
	pst->a[pst->top] = x;
	pst->top++;
}

//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	//判断栈是否为空
	assert(!StackEmpty(pst));
	//出栈
	pst->top--;
}

//获取栈顶元素
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	//top指向的是栈顶的下一个位置的元素
	return pst->a[pst->top-1];
}

//获取栈中的有效元素的个数
int StackSize(Stack* pst)
{
	assert(pst);

	return pst->top;
}

//检测栈是否为空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	/*if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/
	return pst->top == 0;
}

//销毁栈
void StackDestroy(Stack* pst)
{
	assert(pst);

	//释放
	free(pst->a);
	pst->a = NULL;
	//重置为0
	pst->top = pst->capacity = 0;
}

创建队列:

typedef struct {
    Stack STPush;  //用来出数据的栈
    Stack STPop;   //用来入数据的栈
} MyQueue;


MyQueue* myQueueCreate() {
    //先为队列开辟一块空间
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    if(obj == NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    //再初始化栈
    StackInit(&obj->STPush);
    StackInit(&obj->STPop);
    return obj;
}

3.2入列

插入数据就非常简单了,直接将数据插入到我们指定的STPush这个栈中:
代码演示:

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

3.3出列

栈的出栈是先进后出,我们有两个栈,因此可以将STPush栈中的前n-1个数据先依次插入到STPop栈中,然后将剩下的最后一个元素,对于队列来说就是队头的数据进行删除即可,但是呢,当第二次删除的时候STPush中没有数据了,这时的STPop中的数据的排列刚好是队列的这种效果,所以可以直接在STPop栈中直接删除即可,所以出列操作需要分两种情况:STPop栈中有无数据

 

代码演示:

 


int myQueuePop(MyQueue* obj) {
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //导数据
        while (StackSize(&obj->STPush) > 1)
		{
			StackPush(&obj->STPop, StackTop(&obj->STPush));
			StackPop(&obj->STPush);
		}
        //保存队头元素
        int top = StackTop(&obj->STPush);
        //删除
        StackPop(&obj->STPush);
        return top;
    }
    else
    {
        int top = StackTop(&obj->STPop);
		StackPop(&obj->STPop);
		return top;
    }
}

3.4获取队头元素

 获取队头元素这里就比较有点麻烦了,先要知道队头元素在哪个栈中,由于我们刚开始设定的缘故,队头的数据是在STPop这个栈中的,这时就需要分两种情况,如果STPop为空,这时就要将STPush中的数据全部插入到STPop中,插入过来之后STPop中的栈顶的数据就是对头的数据,如果STPop不为空,就不需要进行导数据,STPop中的栈顶元素也就是对头的数据:

代码演示: 

int myQueuePeek(MyQueue* obj) {
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //全部导过去
        while (!StackEmpty(&obj->STPush))
		{
			StackPush(&obj->STPop, StackTop(&obj->STPush));
			StackPop(&obj->STPush);
		}
    }
    //再取栈顶元素
    return StackTop(&obj->STPop);
}

3.5优化代码

如果将出列和获取队头元素这两个函数合在一起看的话还是有许多相似之处,因此我们可以进行改造,在出列时可以直接复用获取队头元素这个函数,这时表现出来的结果就是将STPush中的所有元素都挪动到STPop中,然后再进行删除即可:

 

代码演示:

int myQueuePop(MyQueue* obj) {
    /*
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //导数据
        while (StackSize(&obj->STPush) > 1)
		{
			StackPush(&obj->STPop, StackTop(&obj->STPush));
			StackPop(&obj->STPush);
		}
        //保存队头元素
        int top = StackTop(&obj->STPush);
        //删除
        StackPop(&obj->STPush);
        return top;
    }
    else
    {
        int top = StackTop(&obj->STPop);
		StackPop(&obj->STPop);
		return top;
    }
    */

    //复用
    int front = myQueuePeek(obj);
    //删除
    StackPop(&obj->STPop);
    return front;
}

int myQueuePeek(MyQueue* obj) {
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //全部导过去
        while (!StackEmpty(&obj->STPush))
		{
			StackPush(&obj->STPop, StackTop(&obj->STPush));
			StackPop(&obj->STPush);
		}
    }
    //再取栈顶元素
    return StackTop(&obj->STPop);
}

3.6检测队列是否为空

队列若为空就表示两个栈都为空,若一个栈不为空,则队列就不为空:

代码演示:

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->STPush)
		&& StackEmpty(&obj->STPop);
}

3.7销毁队列 

要销毁队列先要销毁两个栈,然后再将队列释放:

代码演示:

void myQueueFree(MyQueue* obj) {
    //先释放栈
    StackDestroy(&obj->STPush);
    StackDestroy(&obj->STPop);
    //再释放队列
    free(obj);
}

4.完整代码


//动态栈
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;        //栈顶
	int capacity;  //栈的容量
}Stack;


//对栈的初始化
void StackInit(Stack* pst);

//入栈
void StackPush(Stack* pst, STDataType x);

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

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

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

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

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

//对栈的初始化
void StackInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	//top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
	//pst->top = -1     
	//top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
	pst->top = 0;
	pst->capacity = 0;
}

//入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	//检测容量
	if (pst->top == pst->capacity)
	{
		int NewCapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		//当pst->a为NULL时执行的功能是和malloc一样
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = NewCapacity;
	}
	//入栈
	pst->a[pst->top] = x;
	pst->top++;
}

//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	//判断栈是否为空
	assert(!StackEmpty(pst));
	//出栈
	pst->top--;
}

//获取栈顶元素
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	//top指向的是栈顶的下一个位置的元素
	return pst->a[pst->top-1];
}

//获取栈中的有效元素的个数
int StackSize(Stack* pst)
{
	assert(pst);

	return pst->top;
}

//检测栈是否为空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	/*if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/
	return pst->top == 0;
}

//销毁栈
void StackDestroy(Stack* pst)
{
	assert(pst);

	//释放
	free(pst->a);
	pst->a = NULL;
	//重置为0
	pst->top = pst->capacity = 0;
}


typedef struct {
    Stack STPush;  //用来出数据的栈
    Stack STPop;   //用来入数据的栈
} MyQueue;


MyQueue* myQueueCreate() {
    //先为队列开辟一块空间
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    if(obj == NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    //再初始化栈
    StackInit(&obj->STPush);
    StackInit(&obj->STPop);
    return obj;
}

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

int myQueuePop(MyQueue* obj) {
    /*
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //导数据
        while (StackSize(&obj->STPush) > 1)
		{
			StackPush(&obj->STPop, StackTop(&obj->STPush));
			StackPop(&obj->STPush);
		}
        //保存队头元素
        int top = StackTop(&obj->STPush);
        //删除
        StackPop(&obj->STPush);
        return top;
    }
    else
    {
        int top = StackTop(&obj->STPop);
		StackPop(&obj->STPop);
		return top;
    }
    */

    //复用
    int front = myQueuePeek(obj);
    //删除
    StackPop(&obj->STPop);
    return front;
}

int myQueuePeek(MyQueue* obj) {
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //全部导过去
        while (!StackEmpty(&obj->STPush))
		{
			StackPush(&obj->STPop, StackTop(&obj->STPush));
			StackPop(&obj->STPush);
		}
    }
    //再取栈顶元素
    return StackTop(&obj->STPop);
}

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->STPush)
		&& StackEmpty(&obj->STPop);
}

void myQueueFree(MyQueue* obj) {
    //先释放栈
    StackDestroy(&obj->STPush);
    StackDestroy(&obj->STPop);
    //再释放队列
    free(obj);
}

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

今天的博客就分享到这里,喜欢的老铁留下你的三连,感谢感谢!我们下期再见!! 

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

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

相关文章

使用 compose 封装一个通用的关于页面库

前言 现在很多 APP 都会有一个关于页面,用于放置一些必要的信息,例如:版本号、版权信息等。有时也会用于展示设置、帮助、反馈等功能的入口。 通常,我们都会自己挨个创建不同的 item ,略显繁琐。 所以我就在想&…

ad18学习笔记一

如何自学altium designer如何自学altium designer? - 知乎如何自学altium designer 这里面有ad官方推荐的b站的视频:可以直接去b站关注ad官方账号 AltiumChina,它本身就发布了很多实用教程。 在知乎的这个界面也有Altium Designer Ver18_官…

万字长文,为你送上全网最全Flutter学习资料!

话不多说直接上目录,干货较多内容很长,建议先收藏供以后慢慢查阅。 目录 文章视频组件导航模板插件框架实验性游戏开源App实用工具社区书籍福利 文章 介绍 Google IO 2018 [1.1K👏] - 构建美观,灵活的用户界面。Presentation …

Stm32待机模式的进入与唤醒

1.基础介绍 1-1:单片机的“低功耗模式”,像是手机的待机模式,不同于正常运行模式,处于一种省电省资源的状态 1-2:在运行情况下,HCLK为cpu提供时钟,cortex-m3内核执行程序的代码,如…

CleanMyMac X2023Mac上下载最多的第三个实用程序

CleanMyMac X是一款广为人知的Mac优化应用程序,目前是Mac上下载最多的第三个实用程序,并获得苹果官方认证。为了满足用户更好体验Mac和新版系统,它们带来了新功能。这新功能可以帮助用户更好的监控Mac的健康状况,让用户畅享Mac新系…

base编码

https://www.qqxiuzi.cn/bianma/base64.htm 一眼就解密 一看就晓得是base64,问就是做多了(base64大小写加数字和) base编码概念 此博客列举的比较多,我信 https://blog.csdn.net/qq_53105813/article/details/127626450 简单聊几个…

少儿编程 中国电子学会图形化编程等级考试Scratch编程三级真题解析(选择题)2023年3月

2023年3月scratch编程等级考试三级真题 选择题(共25题,每题2分,共50分) 1、计算“2+4+8+……+128”,用变量n表示每项,根据变化规律,变量n的赋值用下列哪个最合适 A、 B、 C、 D、 答案:D 考点分析:考查数学逻辑推理能力,从给定的算式中可以看出后一项都是前一…

redis 数据库概述

一 概述 redis是一种nosql数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,并且他比memcached支持更多的数据结构(string,list列表[队列和栈],set[集合],sorted set[有序集合],hash(hash表))。相关…

基于springboot篮球竞赛预约平台

开发技术与环境配置 以Java语言为开发工具,利用了当前先进的springboot框架,以MyEclipse10为系统开发工具,MySQL为后台数据库,开发的一个篮球竞赛预约平台。 SpringBoot框架 SpringBoot是一个全新开源的轻量级框架。基于Spring…

Callback CBFS .NET 2022所有产品打包Crack

Callback CBFS .NET 所有产品打包 Callback Technologies CBFS Storage 2022 .NET Edition v22.0.8517 Callback Technologies CBFS Filter 2022 .NET Edition v22.0.8485 Callback Technologies CBFS Disk 2022 .NET Edition v22.0.8464 Callback Technologies CBFS Connect …

UART-STM32

UART-STM32 通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统 通信协议:制定通信的规则,通信双方按照协议规则进行数据收发 第一步,开启时钟,把需要用的USART和GPIO的时钟打开 第二步,GPIO初始化,把TX配置成复用输出,RX配置成输入 第三步,配置USART,直接使…

Kanzi Studio介绍与安装注意

文章目录 Kanzi介绍部署 Kanzi 应用程序到 Windows Kanzi介绍 Kanzi UI Solution是一个完整的UI解决方案,为嵌入式的UI的设计、开发和部署提供了一套完善的开发平台。它成功解决了实现和众多图形化用户界面分化的问题,使得用户界面的设计者和编程者可以…

vue的搭建以及命令

0.0 课程介绍 Vue介绍 【了解】 Vue项目的搭建 【掌握】 项目目录详解【掌握】 Vue组件化【掌握】 Vue的表达式 【重点】 Vue的指令【重点】 1.0 什么是Vue?【了解】 Vue是一个构建用户界面(UI)的JS库。 1.1Vue的特点 小 20k 【大概11000多行】…

HTTPS加密流程详解

文章目录 HTTPS与HTTP的关系HTTPS基本工作过程对称密钥非对称密钥中间人攻击证书 HTTPS与HTTP的关系 HTTPS协议基于HTTP,只是比HTTP多了一个加密层,为什么要加密呢?因为网络传输的过程中,明文传输的数据都有可能被劫持篡改&#…

系统分析师(一)软考简介

目录 1.证书简介2.考试简介3.考试报名4.各地考试机构5.考试要求6.考试教程用书 考试时间: 每年5月的最后一个周六 1.证书简介 ​ 软考全称是计算机技术与软件专业技术资格(水平)考试,是由国家人力资源和社会保障部、工业和信息化部…

【图】最小生成树

最小生成树:构造连通网的最小代价生成树。 最小生成树有两种算法:普利姆算法、克鲁斯卡尔算法。 普利姆(Prim)算法 加点,选择相邻点中边权最小的 需要两个一维数组,一个存权值,另一个存起始点…

MySQL 索引篇

什么是索引,索引就相当于一本书的目录。通过索引可以快速查找到对应的数据。 索引常见面试题: 1. 索引的分类: 按数据结构分类: 按物理存储分类:一般分为聚簇索引(主键索引),二级…

【Java EE 初阶】文件操作

目录 1.什么是文件? 1.在cmd中查看指定目录的树形结构语法 2.文件路径 从当前目录开始找到目标程序(一个点) 返回到上一级目录,再找目标程序(两个点) 2.Java中文件操作 1.File概述 1.属性 2. 构造…

CENTO OS上的网络安全工具(二十二)Spark HA swarm容器化集群部署

在Hadoop集群swarm部署的基础上,我们更进一步,把Spark也拉进来。相对来说,在Hadoop搞定的情况下,Spark就简单多了。 一、下载Spark 之所以把这件事还要拿出来讲……当然是因为掉过坑。我安装的时候,hadoop是3.3.5&a…

Unity Metaverse(七)、基于环信IM SDK实现的好友系统、私聊、群聊

文章目录 🎈 简介🎈 用户管理🎈 好友管理🎈 聊天管理🔸 发送与接收消息🔸 消息处理消息项的对象池管理 🎈 简介 在之前的文章中已经介绍了如何接入环信IM Unity SDK,及基于该SDK实现…