数据结构刷题训练:队列实现栈

news2025/2/14 0:13:24

目录

前言

1. 题目:使用队列实现栈

2. 思路

3. 分析 

3.1 创建栈

3.2入栈

3.3 出栈

3.4 栈顶数据

3.5 判空和 “ 栈 ” 的销毁

 4. 题解

总结


前言

        我们已经学习了栈和队列,也都实现了它们各自的底层接口,那么接下我们就要开始栈和队列的专项刷题训练。


1. 题目:使用队列实现栈

题目描述:

 题目链接:

队列实现栈icon-default.png?t=N6B9https://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);
}

 

总结

        本文队列模拟实现栈,让我们在实践中深入思考了数据结构的本质和应用,为我们的编程能力和问题解决能力提供了锻炼。本期内容到此结束,感谢阅读!

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

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

相关文章

在SPSS中实现数据转置

在使用SPSS开展数据分析的过程中,有时候不可避免需要对数据进行转置处理。 例如Kendall协同系数检验和组内相关系数(ICC)检验这两种方法都可以检验定量数据的一致性程度,但是这两种方法对数据的要求不同。 组内相关系数&#xf…

ArcGIS Pro 基础安装与配置介绍

ArcGIS Pro ArcGIS Pro作为ESRI面向新时代的GIS产品,它在原有的ArcGIS平台上继承了传统桌面软件(ArcMap)的强大的数据管理、制图、空间分析等能力,还具有其独有的特色功能,例如二三维融合、大数据、矢量切片制作及发布…

Blazor 简单组件(2):B_row/B_col 12分隔布局 简单开发

文章目录 前言12分隔布局开发B_col.razorB_col.razor.cssB_row.razorB_row.razor.css 使用案例 前言 Blazor 简单组件(0)&#xff1a;简单介绍 12分隔布局开发 B_col.razor if (Offset ! "0") {<div style" grid-column-start: span (Offset)">&l…

solr迁移到另一个solr中(docker单机)

背景介绍 solr数据迁移&#xff0c;或者版本升级&#xff0c;需要用到迁移&#xff0c;此处记录一下迁移方法以及过程中遇到的问题。我这边使用的是docker环境&#xff0c;非docker部署的应该也是一样的。 solr部署教程 准备工作 ● solrA 版本&#xff1a; 8.11.2 (已有so…

数据分析两件套ClickHouse+Metabase(一)

ClickHouse篇 安装ClickHouse ClickHouse有中文文档, 安装简单 -> 文档 官方提供了四种包的安装方式, deb/rpm/tgz/docker, 自行选择适合自己操作系统的安装方式 这里我们选deb的方式, 其他方式看文档 sudo apt-get install -y apt-transport-https ca-certificates dirm…

三款快速删除未使用CSS代码的工具

针对历史项目或项目中有引入CSS框架&#xff08;如Bootstrap&#xff09;&#xff0c;可能会存在大量的 CSS 样式未被使用。这可能产生一些不良的影响&#xff0c;如&#xff1a; 性能问题&#xff1a; 未使用的CSS会增加页面的加载时间&#xff0c;因为浏览器需要下载并解析…

Field Symbol与数据引用的几个应用

这俩货都是指针。在动态编程里用。 但是从好理解的角度来看&#xff0c;都给他们理解成数据对象。 都得指向其他的数据对象。不过field symbol指的是其他的数据对象内存地址的值。而数据引用只是指向内存地址。 1.Field symbol 当Field Symbol指向了其他的数据对象&#xf…

Java集合知识回顾:从分类到工具类,掌握精髓

文章目录 1. 集合的分类2. Collection 接口3. Map 接口4. 泛型5. Collections 工具类总结 在Java编程世界中&#xff0c;集合是一项极为重要的知识&#xff0c;为我们的程序设计提供了强大的数据结构和处理手段。在本篇文章中&#xff0c;我们将回顾集合的分类以及相关的重要概…

微信小程序实现双向滑动快捷选择价格(价格区间)

实现样子 提示&#xff1a;效果可以自己自定义&#xff0c;自己将文字样式更改为自己项目属性即可 实现达到方法 1、左边为最低价&#xff0c;右边为最高价格&#xff0c;可以拖动左边最低价选择价格。拖动右边为最高价。 2、当两个价格重合时&#xff0c;继续拖动&#xff0…

Vue+SpringBoot项目开发:后台登陆功能的实现(二)

写在开始:一个搬砖程序员的随缘记录文章目录 一、SpringBoot项目的搭建二、数据库配置1、新建数据库2、新建用户表 三、SpringBoot项目的配置 一、SpringBoot项目的搭建 项目搭建传送门&#xff1a;从零开始&#xff0c;SpringBoot项目快速搭建 二、数据库配置 1、新建数据库…

无脑——010 复现yolov8 使用yolov8和rt detr 对比,并训练自己的数据集

1.配置环境 1. 首先去官网下载yolov8的zip https://github.com/ultralytics/ultralytics 存放在我的目录下G:\bsh\yolov8 然后使用conda创建新的环境 conda create -n yolov8 python3.8 #然后激活环境 conda activate yolov8然后安装pytorch&#xff0c;注意 &#xff0c;py…

手把手教你快速实现内网穿透

快速内网穿透教程 文章目录 快速内网穿透教程前言*cpolar内网穿透使用教程*1. 安装cpolar内网穿透工具1.1 Windows系统1.2 Linux系统1.2.1 安装1.2.2 向系统添加服务1.2.3 启动服务1.2.4 查看服务状态 2. 创建隧道映射内网端口3. 获取公网地址 前言 要想实现在公网访问到本地的…

Java数组详解 -- 基础知识与常用操作

文章目录 前言一、初识数组1. 数组的定义2. 数组的特点3. 声明和初始化数组4. 默认初始化值 二、访问和操作数组元素1. 数组的索引和范围2. 通过索引访问数组元素3. 修改数组元素的值 三、数组的长度和属性1. 数组的长度计算2. 数组的长度属性3. 数组越界的错误 四、数组的遍历…

TiktTok推“飞轮计划”,推动社交商务生态发展

2023年&#xff0c;TikTok可谓动作频频&#xff0c;为了快速在海外市场中推进社交电商的发展进程&#xff0c;该平台正在采取多项措施&#xff0c;以实现其全球电子商务业务翻两番的目标&#xff0c;即通过TikTok Shop实现年商品销售总额达200亿美元 。 因此&#xff0c;TikTo…

【Java并发】ThreadLocal的实现原理源码解析

文章目录 1.ThreadLocal是什么?2.ThreadLocald实现原理&源码解析3.ThreadLocal-内存泄露问题 1.ThreadLocal是什么? ThreadLocal 是 Java 中的一个线程局部变量类。它提供了一种在多线程环境下&#xff0c;为每个线程独立存储数据的机制。 并且&#xff1a; ThreadLoca…

iptables端口转发,wireshark抓包分析

app发送请求&#xff0c;到安全交互平台访问服务&#xff0c;app发送请求的ip地址是基站随机分配的&#xff0c;ip地址被拉黑了怎么访问&#xff1f;解决办法&#xff1f; 一开始考虑使用nginx作为代理服务器转发请求&#xff0c;后来在服务器用端口转发解决。 修改nginx配置文…

Spark(39):Streaming DataFrame 和 Streaming DataSet 输出

目录 0. 相关文章链接 1. 输出的选项 2. 输出模式(output mode) 2.1. Append 模式(默认) 2.2. Complete 模式 2.3. Update 模式 2.4. 输出模式总结 3. 输出接收器(output sink) 3.1. file sink 3.2. kafka sink 3.2.1. 以 Streaming 方式输出数据 3.2.2. 以 batch …

gin的占位符:和通配符*

1、用法 在 Gin 路由中&#xff0c;可以使用一个通配符&#xff08;*&#xff09;或一个占位符&#xff08;:&#xff09;来捕获 URL 的一部分。 r.GET("/royal/:id", func(c *gin.Context) {id : c.Param("id")//fmt.Println("into :id")c.Str…

为什么商业基础软件需要开源

Bytebase 本身是一家商业软件公司&#xff0c;而作为最核心资产的代码从 Day 0 却是开源的。同时我们还是 star-history.com 的运营者&#xff0c;大家在各种开源渠道会看到它生成的图&#xff1a; 一直以来&#xff0c;常会被别人问起的一个问题&#xff0c;就是为什么 Byteba…

软件研发的道德情操

作者&#xff1a;许晓斌(晓斌) 两百多年前苏格兰出了一位大哲学家&#xff0c;他的名字叫做亚当斯密。今天人们对他的了解更多是在经济学家这个身份&#xff0c;都认为是他发现了“看不见的手”这一神奇的经济规律&#xff0c;以及他那本著名的《国富论》。然而除了这本书之外&…