【数据结构和算法】---栈和队列的互相实现

目录

  • 一、用栈实现队列
    • 1.1初始化队列
    • 1.2模拟入队列
    • 1.3模拟出队列
    • 1.4取模拟的队列头元素
    • 1.5判断队列是否为空
  • 二、用队列实现栈
    • 2.1初始化栈
    • 2.2模拟出栈
    • 2.3模拟入栈
    • 2.4取模拟的栈顶元素
    • 2.5判读栈是否为空

一、用栈实现队列

具体题目可以参考LeetCode232. 用栈实现队列
首先要想到的是,队列是一种先进先出的结构,而栈是一种先进后出的结构。依此我们可以定义两个栈结构来模拟先进先出,既然要定义两个栈,那么为了方便调用,我们可以将这两个栈结构定义在一个结构体中,如下:

typedef struct {
    ST st1;//栈1
    ST st2;//栈2
} MyQueue;

实现 MyQueue类:

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

1.1初始化队列

我们定义的结构体在主函数外部,为了让每个函数都能用到,那么我们就必须要用malloc来动态开辟空间,这样此结构会被保存在静态区,每次函数调用时便不会销毁此变量,然后再将此结构体中的栈初始化

MyQueue* myQueueCreate() 
{
    MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));//动态开辟结构体变量
    //注意初始化栈的参数为地址
    StackInit(&queue->st1);//初始化栈1
    StackInit(&queue->st2);//初始化栈2
    return queue;
}

1.2模拟入队列

我们可以将栈1作为存数据的栈,那么每次入队列操作就是进栈操作(StackPush(&obj->st1, x);)。

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

1.3模拟出队列

  1. 思路1:
    如果我们用栈1obj->st1来存放数据,在模拟出队列时我们首先要断言栈1不为空,那么当栈1不为空且我们需要出队列头元素时。此时就需要栈2obj->st2来暂存数据,即我们将栈1除栈底的全部元素都出栈并入栈到栈2obj->st2,然后再出栈1最后的元素并返回,这样就模拟了先入先出性质。还需要注意的是在返回最后一个元素前还需要再将所有数据从栈2再入到栈1。逻辑如下:

在这里插入图片描述

  1. 思路2:
    栈1用来存数据,栈2用来出数据。 那么为什么栈2的元素可以直接出呢?当我们需要模拟出队列时,我们可以先将栈1中所以元素出栈并入栈到栈2,这样一来栈2中的top就相当于队列头元素。每次从栈2中出元素时要先判断栈2中是否有元素,若没有,就将栈1中的元素出栈并入栈到栈2中。大致逻辑如下:

在这里插入图片描述

与思路一相比较,思路二栈2无需重新入栈1,还可继续模拟出队列。只能说两种思路各有好处,下列代码实现使用的是思路一:

int myQueuePop(MyQueue* obj) 
{
    assert(obj);
    assert(StackSize(&obj->st1) != 0);//栈1不为空
    ST* empty = &obj->st2;//栈2为空
    ST* noempty = &obj->st1;//栈1不为空
    //将栈1除栈底的所有元素出栈并入栈到栈2
    while(StackSize(noempty) > 1)
    {
        StackPush(empty,StackTop(noempty));
        StackPop(noempty);
    }
    //找到队头
    int ret = StackTop(noempty);
    StackPop(noempty);
    //重新入栈1
    while(StackSize(empty) > 0)
    {
        StackPush(noempty,StackTop(empty));
        StackPop(empty);
    }
    return ret;
}

1.4取模拟的队列头元素

此函数实现与1.3模拟出队列方法相似,就不多介绍了,如下:

int myQueuePeek(MyQueue* obj)
{
    assert(obj);
    ST* empty = &obj->st2;
    ST* noempty = &obj->st1;
    //将栈1除栈底的所有元素出栈并入栈到栈2
    while(StackSize(noempty) > 1)
    {
        StackPush(empty,StackTop(noempty));
        StackPop(noempty);
    }
    //找到队头
    int ret = StackTop(noempty);
    StackPush(empty,ret);
    StackPop(noempty);
    //重新入栈1
    while(StackSize(empty) > 0)
    {
        StackPush(noempty,StackTop(empty));
        StackPop(empty);
    }
    return ret;
}

1.5判断队列是否为空

依据上面思路,因为栈1是用来存数据的,所以当栈1为空时就代表我们模拟的队列为空。

bool myQueueEmpty(MyQueue* obj) 
{
    assert(obj);
    return StackEmpty(&obj->st1);
}

二、用队列实现栈

具体题目可以参考LeetCode225. 用队列实现栈
与用栈实现队列相似,我们同样需要两个队列来模拟实现栈,且关键在于还原队列先入先出的性质,依此性质来实现函数。既然这样我们可以如下定义结构体:

typedef struct 
{
    Queue* q1;//队列1
    Queue* q2;//队列2
} MyStack;

我们可以看到与模拟队列不同的是,模拟栈的结构体中存放的是两个结构体指针,这与队列的实现方法有关。因为我们用的队列是用链表实现的,所以其每个节点都是组成队列的一部分,且我们应该通过指针将他们一个个都连接起来(即通过指针来寻找节点)。
至于用栈实现队列问题中的结构体我们存放的是两个关于栈的结构体,是因为我们所使用的栈使用数组来实现的,这样一来我们操作的就是栈结构体中某一个元素(即动态开辟的数组)。当然在我们也可以放两个栈结构体指针,只不过在下面初始化队列时(myQueueCreate() )我们需要额外malloc动态开辟栈结构大小的空间,然后将指针指向该空间的地址。
实现 MyStack类:

  • void push(int x)将元素 x压入栈顶;
  • int pop()移除并返回栈顶元素;
  • int top()返回栈顶元素;
  • boolean empty()如果栈是空的,返回 true;否则,返回 false

2.1初始化栈

malloc()动态开辟栈结构体没什么问题,与模拟队列相似。但为什么还要给结构体中的两个队列结构体指针动态开辟空间呢?这样不就违背了我们上面探讨的问题了吗?其实不然,这里的两个结构体指针事实上指向的是存放队列头指针和尾指针的结构体,如下:

typedef struct Queue
{
	QNode* phead;//队列头指针
	QNode* ptail;//队列尾指针
	int size;//长度
}Queue;

这样一来,基本每个函数都需要用到此结构体,那么我们就必须malloc开辟来增加作用域和生命周期。 最后再初始化这两个存放头/尾指针的结构体,并返回用来模拟栈的结构体地址。

MyStack* myStackCreate() 
{
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
	pst->q1 = (Queue*)malloc(sizeof(Queue));
	pst->q2 = (Queue*)malloc(sizeof(Queue));
    QueueInit(pst->q1);
    QueueInit(pst->q2);
    return pst;
}

2.2模拟出栈

与模拟出队列不同的是,这里用来模拟出栈的两个队列都可以用来出栈和入栈,具体方法如下:
为了还原栈先入后出的性质,我们可以先找到不为空的队列(因为两个队列都有可能有数据,但不同时有),然后将有数据的队列(noempty)除队尾的一个节点全都出队列并入队列到无数据的队列(empty,这样一来就找到了尾节点(模拟的栈顶)。还需要注意的是,此时我们无需再将数据重新入到noempty 逻辑大致如下:

在这里插入图片描述

int myStackPop(MyStack* obj) 
{
    //先假设队列1为空
    Queue* empty = obj->q1;
    Queue* noempty = obj->q2;
    //纠正
    if(QueueEmpty(obj->q2))
    {
        empty = obj->q2;
        noempty = obj->q1;
    }
    //noempty出,并入到empty
    while(QueueSize(noempty) > 1)
    {
        int cmp = QueueFront(noempty);
        QueuePop(noempty);
        QueuePush(empty, cmp);
    }
    //取到模拟的栈顶元素
	int ret = QueueFront(noempty);
	QueuePop(noempty);
    return ret;
}

2.3模拟入栈

依据上面的方法,我们是要将数据入到不为空的队列,简单的if语句便可完成筛选。

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

2.4取模拟的栈顶元素

同样我们需要找到不为空的那个队列,且事实上队列尾指针指向的那个节点就是模拟的栈的栈顶,我们只需返回此元素即可。

int myStackTop(MyStack* obj) 
{
    assert(obj);
    //找不为空的队列
    if(!QueueEmpty(obj->q1))
        return QueueBack(obj->q1);
    else
        return QueueBack(obj->q2);
}

2.5判读栈是否为空

当两个队列都没有数据时,那么模拟的栈就是空栈。

bool myStackEmpty(MyStack* obj) 
{
    assert(obj);
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

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

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

相关文章

如何有效恢复 Android 上已删除的短信/短信

“ 我昨天不小心删除了小米中的一些重要短信,如何快速恢复它们?我急需他们,请帮帮我!” 峰峰在 Android 论坛中提出的问题。 随着时代的变迁,人们的通讯方式也发生了很大的变化,各种即时通讯软件纷纷涌现…

常用单片机认识

单片机有哪些类型: 51单片机 AVR 单片机 MSP430 STM8 STM32 DSP Linux FPGA

微服务 Spring Cloud 10,如何追踪微服务调用?服务治理的常见手段

目录 一、服务追踪的作用1、优化系统瓶颈2、优化链路调用3、故障排查4、性能优化5、生成网络拓扑图4、透明传输数据 二、节点管理1、服务调用失败一般有两类原因造成:2、服务调用失败的解决方式:3、服务调用失败的具体解决方式: 三、负载均衡…

推箱子地图库1-49关

推箱子地图库1-49关 49关 local WALL1--{"墙","墙 "}4 10287 local DEST2--{"目的地",""}1 4001100 10157 local BOX3--{"箱子","¥"} 2 2000801 local PLAYER4--{"玩家","&&a…

【小白专用】php以pdo方式连接sqlserver,开启sqlsrv扩展

一、安装ODBC程序, 下载适用于 SQL Server 的 ODBC 驱动程序 - 适用于 SQL Server 的 ODBC 驱动程序 |Microsoft 学习 运行安装程序,出现如下图所示页面; 选择下一步;选择我同意许可协议中的条款后选择下一步; 点击安…

C/C++ 外部链接的静态变量 static和extern的应用

外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别有时称为外部存储类别(external storage class),属于该类别的变量称为外部变量(external variable)。把变量的定义性声明放在所有函数的外面便创建了外部变量。当然,为了指出…

15-高并发-如何扩容

对于一个发展初期的系统来说,不太确定商业模型到底行不行,最好的办法是按照最小可行产品方法进行产品验证,因此,刚开始的功能会比较少,是一个大的单体应用,一般按照三层架构进行设计开发,使用单…

开启创意之旅:免费、开源的噪波贴图(noise texture)生成网站——noisecreater.com详细介绍

在当今数字创意领域,噪波贴图(Noise Texture)是游戏渲染、游戏开发、美术设计以及影视制作等行业不可或缺的艺术素材之一。为了满足广大创作者的需求,noisecreater.com应运而生,成为一款免费、开源的噪波贴图生成工具。…

【数据结构】无向图的最小生成树(Prime,Kruskal算法)

文章目录 前言一、最小生成树二、Kruskal算法1.方法:2.判断是否成环3.代码实现 三、 Prim算法1.方法:2.代码 四、源码 前言 连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任意一对…

vue前端上传图片到阿里云OSS,超详细上传图片与视频教程

vue前端直传图片与视频到阿里云OSS 1. 简介与日常使用2. 为什么要这么干?是因为我司后端不行吗???(确实!)3. vue前端直传的操作4. 如何上传到阿里OSS指定文件夹呢? 1. 简介与日常使用 阿里云…

(CVE-2019-9193)PostgreSQL 高权限命令执行漏洞的复现

漏洞概述 PostgreSQL是一个功能强大对象关系数据库管理系统(ORDBMS)。由于9.3增加一个“COPY TO/FROM PROGRAM”功能。这个功能就是允许数据库的超级用户以及pg_read_server_files组中的任何用户执行操作系统命令。 影响版本 9.3-11.2 环境搭建 1. 本次漏洞环境使用vulhub中…

java: -source 7 中不支持 lambda 表达式 (请使用 -source 8 或更高版本以启用 lambda 表达式)

目录 1、检查项目中 JDK 的设置: 2、检查模块中 JDK 的设置: 3、检查Idea 中的SDK设置 4、检查 IDEA 中 JDK 的设置(我出现的问题在这): 今天遇见了一个报错: 问题产生的原因是 JDK 版本太低&#xf…

揭秘2024年最新骨传导耳机排行榜,全面解析骨传导耳机排行榜品牌

随着科技的飞速发展,人们对音质和舒适度的需求也在不断提高。骨传导耳机作为一种独特的耳机类型,近年来逐渐受到了消费者的关注。它通过将声音通过骨骼传导,而不是传统的耳道传递,既能保证音质,又能避免长时间佩戴耳机…

缓存高可用:缓存如何保证高可用?

前面我们提到了缓存集群的负载均衡策略,保证缓存服务的高可用,集群策略是最常用的,本文我们以 Redis 为例,分析一下单点缓存如何扩展到集群,以及集群部署的几种常见模式。 Redis 的主从复制 集群实现依靠副本&#x…

ES-mapping

类似数据库中的表结构定义,主要作用如下 定义Index下的字段名( Field Name) 定义字段的类型,比如数值型、字符串型、布尔型等定义倒排索引相关的配置,比如是否索引、记录 position 等 index_options 用于控制倒排索记录的内容,有如…

人大金仓Kingbase数据库备份和还原

前言 最近在项目开发过程中,使用了国产数据库人大金仓(即Kingbase数据库),在使用过过程中需要对数据库进行备份与还原,在此对相关的命令进行简单介绍,以备不时之需。 Linux环境下安装人大金仓可参考此篇文…

猜数字游戏 C语言xdoj490

问题描述 猜数字游戏是令游戏机随机产生一个 100 以内的正整数,用户输入一个数对其进行猜测,需要你编写程序自动对其与随机产生的被猜数进行比较,并提示大了(“Too big”),还是小了(“Too smal…

C# WPF上位机开发(从demo编写到项目开发)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 C# WPF编程,特别是控件部分,其实学起来特别快。只是后面多了多线程、锁、数据库、网络这部分稍微复杂一点,不过…

Ubuntu 常用命令之 mkfs 命令用法介绍

📑Linux/Ubuntu 常用命令归类整理 mkfs 是在 Linux 和其他 Unix-like 系统中用于创建文件系统的命令。在 Ubuntu 系统中,mkfs 命令也是用于创建文件系统的。mkfs 是一个包装器,它会根据用户指定的文件系统类型调用相应的程序。 mkfs 命令的…

阅读笔记-Minimum margin loss for deep face recognition

Minimum margin loss for deep face recognition 深度人脸识别的最小边缘损失 1、这篇论文要解决什么问题?要验证一个什么科学假设? 以往的损失函数不能解决类不平衡数据集存在的边际偏差问题,即所谓的长尾分布。MS-Celeb-1M和megface都存…