栈和队列的习题详解(2):用队列实现栈

news2024/11/5 9:40:13

前言:

  小编在上一篇博客写了栈和队列其中一个习题,为了体现出题目的重要性所以我把每个题目都分开写了,下面废话不多说,开启我们今天的做题之旅~


目录

1.用队列实现栈

1.1.题目介绍

1.2.做题方法介绍

1.3.栈功能的实现

 1.3.1.栈的初始化(MyStack* myStackCreate())

 1.3.2.入栈操作(myStackPush(MyStack* obj, int x)) 

 1.3.3.判断栈是否为空(bool myStackEmpty(MyStack* obj)) 

 1.3.4.出栈操作(void myStackPush(MyStack* obj, int x)) 

 1.3.5.取栈顶操作( myStackTop(MyStack* obj))

1.3.6.栈的销毁(void myStackFree(MyStack* obj)) 

1.4.代码展示 

2.总结


正文:

1.用队列实现栈

  老规矩,先展示一下这个题目:225. 用队列实现栈 - 力扣(LeetCode)

1.1.题目介绍

  这个题目的要求是让我们用队列实现栈,到这里可能有很多读者朋友会疑惑,栈和队列我们之前也实现过,我们也知道他俩是截然不同的两个结构,栈是后进先出的结构,队列是先进先出的结构,可这题却想让我们用两个队列实现一个栈,小编当时对这个题是只有一点思路的,我当时想着把两个队列“合并起来”,让一个队列负责出数据,一个队列负责进数据,之后我就没思路了,于是我就开始听取老师的讲解,在听完讲解后,我觉着这个思路也是蛮厉害的,下面小编就先给各位读者朋友讲述一下这个题目的其中一个解题方法~

1.2.做题方法介绍

  此时题目给我们的就是两个队列,小编这个做题方法简单来说就是来回倒数据;首先,我们需要直接把队列我们实现的功能复制到这个题目来,然后我们需要先设置好我们的自己栈的结构体,由题目我们就可以知道我们仅需创建两个队列就好;然后我们就要进行栈的初始化操作,这里我们首先要动态开辟一个栈类型的指针,然后我们在把栈里面两个队列初始化就好了,此时就实现了初始化操作;然后就是入栈操作,此时就是涉及到了倒数据,我们可以在进行入栈操作的时候,先把数据放入一个不为空的队列;然后在想要出栈的时候,我们可以让不为空的队列里面除了最后一个数据全部放入到空的队列,此时涉及到了队列的取队头操作和出队列操作,之后我们保存好最后一个元素的数据后,在进行出队列操作,此时我们就可以实现出栈操作;当然对于栈,我们还有取栈顶的操作,此时我们可以直接返回不为空队列的队尾元素,此时的队尾其实就是栈的栈顶,毕竟栈顶是最后一个进入的数据;当然,我们在进行出栈或者取栈顶操作的时候,还需要去判断这个栈是否为空,所以我们需要写一个判断函数,此时这个判断函数就是判断两个队列是不是全空的,如果是全空的,则可以说这个栈是没有数据的,我们就不可以执行这两个操作,至于为什么是全空,小编在前面说过,我们插入是往不为空的队列插入数据,我们需要保证一个队列是空的,这样才可以进行出栈操作;最后,我们肯定要写栈的销毁操作,这里我们直接调用队列的销毁操作把两个队列销毁后,然后我们在把新创建的自己的栈结构free掉然后设置为空指针,这样我们便可以写完这个题目,肯定很多读者朋友和我一样,知道了思路但是代码不太会打,下面小编将讲述这些功能的代码实现!

1.3.栈功能的实现

  在写功能之前,小编先把这个结构体的内容代码先呈现出来,防止各位读者朋友不清楚:

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;
 1.3.1.栈的初始化(MyStack* myStackCreate())

  初始化操作刚才小编也简单的说了一下,由于此时的函数的返回值是MyStack*,意味着我们需要新建一个栈类型的指针然后返回指针,所以此时我们就用到了动态内存的知识,我们需要动态开辟一块栈类型的空间,创建完以后我们需要对里面的内容进行初始化,对于里面的初始化,我们直接去调用它自己的初始化函数就好,之后我们就完成了栈的初始化,然后返回新创立的指针即可,代码如下图所示:

MyStack* myStackCreate() {
    MyStack* pq = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pq -> q1);
    QueueInit(&pq -> q2);
    return pq;
}
 1.3.2.入栈操作(myStackPush(MyStack* obj, int x)) 

  对于入栈操作,我们需要把我们想要的元素放入到一个不为空的队列中,我们需要留出一个空的队列来进行倒数据操作,所以此时我们需要用到if语句来帮助我们去实现寻找不为空的队列,此时我们可以通过我们在写队列时查询队列元素是否为空的函数来帮助我们寻找,我们直接先查看第一个队列是否为空,如果不为空的话,我们直接把数据放入到第二个队列,这里是要用if else语句,千万不要用两个if语句,因为刚开始的时候两个队列都是空的,如果此时用两个if语句我们就无法入队列了,之后我们寻找到不为空的队列以后,我们可以调用入队列的函数来实现数据进入队列,此时我们就完成了入栈操作,下面是代码展示图:

void myStackPush(MyStack* obj, int x) {
    if(!Queuepanduan(&obj -> q1))
    QueuePush(&obj -> q1,x);
    else
    {
        QueuePush(&obj -> q2,x);
    }
}
 1.3.3.判断栈是否为空(bool myStackEmpty(MyStack* obj)) 

  在写出栈操作的时候,我们首先需要判断栈中是否有元素,对于此函数,小编在前面也简单说过,此时如果栈中是有元素的,那么其中一个队列指定是有元素的,所以此时判空的条件就是两个队列均为空,此时就说明了这个栈是空的,下面直接展示代码:

bool myStackEmpty(MyStack* obj) {
    return Queuepanduan(&obj ->q1 ) && Queuepanduan(&obj -> q2);
}
 1.3.4.出栈操作(void myStackPush(MyStack* obj, int x)) 

  出栈操作算是一个栈功能代码不太容易写的函数了,前面也说过,这里我们就涉及到了倒数据这一个操作,首先我们需要先找到不为空的队列和空的队列,此时我们也不知道是哪一个队列是空的,所以小编给出的操作就是我们先默认设置两个队列结构体指针,第一个队列是不空队列指针,第二个队列是空队列指针,之后我们在通过if语句来判断第一个队列是否为空,如果为空我们在把让不为空队列是第二个,空为第一个,这里我们就完成了寻找不为空队列的操作,之后我们就要通过循环,来开始让不为空队列的数据转移到空队列中,对于这方面的操作小编就通过图文来进行更好的解释,此时我们先默认不为空里面的元素是1,2,3。

  刚开始,我们先通过取栈顶的操作设置一个新的变量来存储队头数据,之后我们把队头的数据放入到空的队列中,之后我们在进行出队列操作,此时空队列已经有1这个数据了,如下图所示:

  之后我们继续重复进行上面的操作,直到原来的不为空队列中只有1个数据,如下图所示:

   此时左边队列中仅存的一个数据,这个数据就是最后一个进入队列的数据,自然也就是栈顶数据,因为栈是后进先出,最后一个进的数据第一个出来,所以此时我们先设置一个变量保存这个数据,之后直接让这个数据出队列,然后返回这个变量就好了,代码如下图所示:

int myStackPop(MyStack* obj) {
    assert(!myStackEmpty(obj));
    Queue* empty = &obj -> q1; //空队列
    Queue* nonety = &obj -> q2;
    if(!Queuepanduan(&obj -> q1))
    {
        empty = &obj -> q2;
        nonety = &obj -> q1;
    }
    while(QueueSize(nonety) > 1)
    {
        int front = QueueFront(nonety);
        QueuePush(empty,front);
        QueuePop(nonety);
    }
    int a = QueueFront(nonety);
    QueuePop(nonety);
    return a;
}
 1.3.5.取栈顶操作( myStackTop(MyStack* obj)

  取栈顶操作相比于上面的操作算是比较简单的了,可能很多读者朋友写这个函数的时候,会巧妙的把上面出栈操作的函数复制过来,因为出栈操作的最后有一个队列是保存着栈顶的元素,我们不在进行出栈操作,直接返回栈顶元素就完成了这个函数,乍一看,这么说还真是没毛病,但是,还记着小编在开头说过的话吗,我们这个题目的主旨就是倒数据实现出栈操作,必须有一个队列是空的,此时如果这么操作的话,两个队列都会有数据,我们在进行入栈操作的时候会随机往其中一个队列放入数据,此时这个题目就乱套了,我们在出栈操作的时候两个队列都有数据,我们就无法在实现倒数据的操作了,所以这个做法是错误的,小编当时也是犯了这样一个错误,找了好久才直到错因,下面小编说一下这个操作的正常实现流程:

  其实我们如果想要取栈顶元素,无非就是想要寻找队列最后一个元素,此时我们就用到了我们在写队列的时候写过的取队尾数据,此时我们可以直接取不为空队列的队尾,此时的队尾就是栈顶元素,所以此时我们还需要寻找不为空的队列,寻找方法和入栈操作的寻找方式是一样的,当然,在进行这一切操作的前提下,就是这个栈是不为空的,所以我们还要判断栈是否为空,此时我们就成功的实现了取栈顶操作,仔细看看小编说的,其实这个函数难度不大,各位读者朋友可千万不要犯上面的错误,下面小编将展示代码:

int myStackTop(MyStack* obj) {
    assert(!myStackEmpty(obj));
    if(!Queuepanduan(&obj -> q1))
    {
        return QueueBack(&obj -> q1);
    }
    else
    {
        return QueueBack(&obj -> q2);
    }
}
1.3.6.栈的销毁(void myStackFree(MyStack* obj) 

  栈的销毁操作其实就是把我们开辟的空间给free掉,对于栈中的队列,我们可以采取用他们各自的销毁操作来实现,在销毁完以后,不要忘记我们的obj也是动态开辟出来的,此时我们需要free掉obj,然后让它指向空就好,养成好习惯,尽量不要写出野指针,此时销毁操作也是完成了,下面上代码:

void myStackFree(MyStack* obj) {
    QueueDestroy(&obj -> q1);
    QueueDestroy(&obj -> q2);
    free(obj);
    obj = NULL;
}

  此时我们已经实现了全部的功能,肯定有读者朋友想要知道这个题的代码怎么写,下面小编将会展示这个题目的完整代码。 

1.4.代码展示 

  

typedef int QDataType;
typedef struct list {                //表示单链表
	QDataType data;
	struct list* next;
}list;

typedef struct Queue {
	list* phead;
	list* plist;
	int size;
}Queue;
void QueueInit(Queue* pq)
{
	pq->phead = pq->plist = NULL;
	pq->size = 0;
}



list* Queuebuynode(x)
{
	list* p1 = (list*)malloc(sizeof(list));
	assert(p1);
	p1->next = NULL;
	p1->data = x;
	return p1;   //别忘记返回新节点,tnnd我记着单链表我也是这样错的
}


void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	if (pq->phead == NULL)   //先判断头结点是不是存在的
	{
		pq->phead = pq->plist = Queuebuynode(x);
	}
	else
	{
		list* newnode = Queuebuynode(x);
		pq->plist->next = newnode;
		pq->plist = newnode;
	}
    pq->size++;
}



bool Queuepanduan(Queue* pq)
{
	assert(pq);
	return pq->phead == NULL && pq -> plist == NULL;
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!Queuepanduan(pq));
	if (pq->phead == pq->plist)
	{
		free(pq ->phead);
		pq->phead = pq->plist = NULL;
	}
	else
	{
		list* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
    pq-> size--;
}




QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!Queuepanduan(pq));
	return pq->phead->data;
}


QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!Queuepanduan(pq));
	return pq->plist->data;
}


int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}



void QueueDestroy(Queue* pq)
{
	assert(pq);
	//assert(!Queuepanduan(pq));
	list* p1 = pq -> phead;
	while (p1)
	{
		list* next = p1 -> next;
		free(p1);
		p1 = next;
	}
	pq->phead = pq->plist = NULL;
	pq->size = 0;
}



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


MyStack* myStackCreate() {
    MyStack* pq = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pq -> q1);
    QueueInit(&pq -> q2);
    return pq;
}
bool myStackEmpty(MyStack* obj) {
    return Queuepanduan(&obj ->q1 ) && Queuepanduan(&obj -> q2);
}

void myStackPush(MyStack* obj, int x) {
    if(!Queuepanduan(&obj -> q1))
    QueuePush(&obj -> q1,x);
    else
    {
        QueuePush(&obj -> q2,x);
    }
}

int myStackPop(MyStack* obj) {
    assert(!myStackEmpty(obj));
    Queue* empty = &obj -> q1; //空队列
    Queue* nonety = &obj -> q2;
    if(!Queuepanduan(&obj -> q1))
    {
        empty = &obj -> q2;
        nonety = &obj -> q1;
    }
    while(QueueSize(nonety) > 1)
    {
        int front = QueueFront(nonety);
        QueuePush(empty,front);
        QueuePop(nonety);
    }
    int a = QueueFront(nonety);
    QueuePop(nonety);
    return a;
}

int myStackTop(MyStack* obj) {
    assert(!myStackEmpty(obj));
    if(!Queuepanduan(&obj -> q1))
    {
        return QueueBack(&obj -> q1);
    }
    else
    {
        return QueueBack(&obj -> q2);
    }
}


void myStackFree(MyStack* obj) {
    QueueDestroy(&obj -> q1);
    QueueDestroy(&obj -> q2);
    free(obj);
    obj = NULL;
}

2.总结

  以上便就是这个题目的解题流程,这个题目很重要,各位读者朋友一定要去好好的理解,可能现在读者朋友会很好奇,我们都可以用队列实现了栈了,那么我们是否可以在用栈实现队列呢?答案是肯定的,小编准备下一篇博客就讲述用栈实现队列,大家敬请期待,小编算是一个编程界的小白,如果文章由错误,大家可以在评论区指出,小编一定会及时回答并更正,那么,我们下一篇文章见啦!

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

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

相关文章

天聚数行®近期上线了六个实用的API接口

天聚数行近期上线了一系列实用的API接口服务,涵盖了多种场景下的数据处理和信息查询的需求,为企业和开发者带来了便捷高效的工具支持。这些服务包括工商信息查询、手机状态检测(如在网状态和空号检测)、坐标系转换等功能&#xff…

飞利浦的精益转型之路:从传统制造到智能制造的华丽蜕变

飞利浦作为一家拥有百年历史的全球知名品牌,其在精益转型方面的经验值得我们深入研究和借鉴。本文将从飞利浦的转型背景、转型过程、转型成效以及给我们的启示等方面,探讨飞利浦如何成功实现精益转型,从而在新的市场竞争中脱颖而出。 一、转型…

沐渥科技:两显氮气柜和三显氮气柜要怎么选择?

两显氮气柜通常指的是控制面板上有两个LED数码显示界面,用于显示温度和湿度;三显氮气柜则有三个LED数码显示界面,能够直观地显示出温度、湿度和含氧量。这样的设计便于用户快速全面地了解柜内环境状态,不需要额外的操作即可掌握所…

录屏神器!这四款免费版助你轻松成为剪辑大师

在数字化的时代,录屏软件已经成为了我们工作和学习中的得力助手。对于需要记录电脑屏幕操作,或是制作教学视频、游戏解说等内容的用户来说,一款好用的录屏软件是必不可少的;这篇文章将分享四款免费录屏软件: 第一款&a…

深度学习(九)-图像形态操作

仿射变换 仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。平直性是指图像经过仿射变换后,直线仍然是直线;平行性是指图像在完成仿射变换后,平行线仍然是平行线。 平移 镜像 旋转…

spark读取csv文件

测试spark读取本地和hdfs文件 from pyspark.sql import SparkSessionspark SparkSession.builder \.appName("Example PySpark Script") \.getOrCreate()# 读取本地csv文件 df spark.read.csv("/Users/xiaokkk/Desktop/local_projects/spark/intents.csv&quo…

三诺动态血糖管理在医院中的应用

一、引言 随着糖尿病患病率的逐年上升,糖尿病管理已成为医疗体系中的重要挑战。特别是在医院环境中,针对重症及需要精细化治疗的患者,动态血糖管理显得尤为重要。传统血糖监测手段的局限性日益凸显,而三诺生物传感股份有限公司&am…

国产航顺HK32F030M:WS2812 炫彩LED灯驱动笔记(C51/STM32/HK32)

WS2812B参数 3528 幻彩雾状 贴片式发光二极管 XL-3528RGBW-WS2812B

批处理常用指令与脚本的例子

另:win7扩展名显示 一、常用指令 1、REM 和:: 2、ECHO和 3、PAUSE 4、ERRORLEVEL 5、TITLE 6、COLOR 7、mode 配置系统设备 8、GOTO和: 9、FIND 10、START 11、assoc和 ftype 12、pushd和 popd 13、CALL 14、shift 15、IF 16、setlocal 与变量延迟…

Ethernet 系列(2)-- 物理层测试::IOP Test::Signal Quality

车载以太网物理层IOP测试,即互操作性测试(Interop- erability Tests),用于验证车载以太网PHY(通常也称为收发器)的可靠性和检查PHY能否在给定的有限时间内建立稳定的链路;还用于车载以太网PHY的诊断&#x…

利用SSH加密实现的HTTP隧道分析与检测

1.隧道介绍 Chisel是一个快速稳定的TCP/UDP隧道工具,该工具基于HTTP实现,并通过SSH加密保证通信安全。Chisel可以进行端口转发、反向端口转发以及SOCKS流量代理,使用GO语言编写,具备较好的跨平台特性。该工具的主要用于绕过防火墙…

已过售后期客服话术分享

在电商运营中,我们经常会遇到一些过了售后期还来找客服的买家。他们的问题五花八门,有的真的是质量问题,有的则可能是使用不当或者其他原因导致的。面对这样的情况,我们应该如何正确处理呢?下面给大家分享一些技巧和已…

构建高效医护人员排班系统:Spring Boot框架的优势

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理医护人员排班系统的相关信息成为必然。开发…

上海晋名室外危化品暂存柜助力新能源行业发展

近日又有一个SAVEST室外危化品暂存柜项目成功验收交付使用。 用户在日常经营活动中涉及到气瓶和硅粉的室外安全暂存问题,4月下旬在网上看到上海晋名室外暂存柜系列很感兴趣,联系到了销售部钟经理,双方对晋名的室外暂存柜进行了高效的沟通&am…

AI在医学领域: Oralytics 改善口腔自我保健行为

牙齿疾病是一种常见的慢性疾病,与糖尿病、心血管疾病等全身性疾病有关,对患者造成痛苦并带来沉重的经济负担。美国牙科协会推荐每天刷牙两次,每次两分钟,以预防牙齿疾病。然而,由于遗忘和缺乏动力等因素,患…

期权盈亏怎么计算?期权怎么才能盈利?

今天带你了解期权盈亏怎么计算?期权怎么才能盈利?期权交易盈利和亏损的计算公式为:盈亏额 (行权价格 权利金) - 买入权利金。 50ETF期权盈亏 第一种情况,提前平仓情况下的盈亏计算:平仓权利…

P1.8COB小间距成本下降,增大COB超微小间距LED显示屏市场份额

随着P1.8 COB(Chip On Board)小间距技术的不断成熟与成本的有效控制,其在LED显示屏市场中的竞争力日益凸显。这一趋势不仅激发了行业内对超微小间距LED显示屏的浓厚兴趣,更推动了市场的快速扩张。 随着成本的进一步下降&#xff…

【Java】实体类Javabean的运用案例

文章目录 前言一、定义一个操作类专门处理数据二、代码总结 前言 实体类Javabean的运用案例,现在需要把数据与业务串联起来。 一、定义一个操作类专门处理数据 这里定义了一个叫DogOperator的类,专门用来处理Dog类里面的数据。 解析: 要把…

揭秘北斗GPS车载终端有什么功能?

北斗GPS车载终端一款基于北斗卫星导航系统的高科技车载设备,专为现代交通管理和智能出行设计。集定位、导航、监控和通信于一体,通过精准的北斗定位系统,为车辆提供全天候、全方位的位置服务。本文将带您深入了解北斗终端的魅力,探…

淘宝订单 API 接口:获取淘宝平台数据的 api 接口(电商 ERP 订单对接方案)

taobao.seller_order_list 获取卖家订单列表 taobao.custom 自定义淘宝开放平台 API 公共参数 请求地址: 登录 - 凡邦跨境电商平台接口提供商 数据采集公司 数据接口定制服务 企业级数据服务商 seller_order_list 参数说明 请求参数 请求参数:page&tabCode&…