栈和队列的转换

news2024/11/22 17:52:56

  在之前的博客当中我们已经学习了栈和队列。在本次的博客当中我们就来学习一下怎么将栈和队列进行相互转换。

  栈和队列的相互转换其实是两道OJ题。如果在leetcode上面刷过题的小伙伴们可能早就见过这两种数据结构的相互转换。下面我们就来分别讲解一下这两道OJ题目的编写思路。

    🌵用队列实现栈

题目详情如下:

bd2fc494ee6b41dd9f8a39df07f85587.png   题目中要求我们使用两个队列实现栈的结构。我们在这里重新来梳理一下队列和栈的结构特点。

    ⭐队列:队列只能在队尾插入数据,只能从队头输出数据。先进入队列的数据也就会先被读取。 

39901254e458435d838e82d789a132ff.png      ⭐栈:栈只能从队头插入数据,也只能从对头输出数据。所以在咋还能当中我们只能够取出最近插入的数据。

596144df079a48b7877756115e9e431f.png   在结构上面栈和队列还是有很大的区别的,所以我们需要使用一定的方式进行栈和队列的相互转换。回到我们本题。

  使用队列实现栈。也就是利用我们先入先出的数据存储方式实现先入后出的数据存储方式。题目中也给了我们一些相应的思路:使用两个队列。

  我们先来构建两个队列:

d0daae3ccf6a4236821b304c6bdd84c6.png   我们直接来说解题思路:我们构建完成两个队列之后可以尝试向一个队列当中插入数据。当我们想要取出数据的时候,根据栈的性质我们需要取得我们队列当中位于队尾的数据。但是又根据队列的性质我们只允许从对头拿出数据,所以我们可以先将队列1当中的数据除了最后一个之外都全部拿到另一个空队列当中。897fd9d80d9242088a38d44ec5da5e48.png  当我们的队列当中是剩下最后一个元素的时候,这个元素就是我们队列当中的队尾元素,也就是栈中需要出栈的元素。我们可以所以我们如果想要出栈的话只需要重复上述的操作即可。fc30231c179845b3bb5f7714704b4007.png   所以在队列实现栈的代码的编写当中我们需要实现一个队列的结构,进而构建出两个队列,之后根据上面分析的思路进一步的编写代码。其中我们的队列可以复用以前实现过的队列当中的代码。


typedef int DataType;

typedef struct QNode
{
	struct QNode* next;
	DataType data;
}QNode;

typedef struct Queue
{
	struct QNode* head;
	struct QNode* tail;
	int size;
}Queue;

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

//开辟一个新的节点
QNode* BuyNewNode(DataType x)
{
	QNode* ret = (QNode*)malloc(sizeof(QNode));
	if (ret == NULL)
	{
		perror("malloc");
		return NULL;
	}
	ret->data = x;
	ret->next = NULL;
	return ret;
}

//向队列当中插入数据
void QueuePush(Queue* pq, DataType x)
{
	assert(pq);
	QNode* newnode = BuyNewNode(x);
	assert(newnode);
	if (pq->size == 0)
	{
		pq->head = newnode;
		pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}

//删除队列当中的数据
void QueuePop(Queue* pq)
{
	assert(pq);
	if (pq->size == 0)
	{
		printf("队列为空不能进行数据删除。");
		return;
	}
	QNode* ret = pq->head;
	pq->head = pq->head->next;
	free(ret);
	ret = NULL;
	pq->size--;
}

//判断队列的大小
DataType QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

//队列的判空操作
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	if (pq->size == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

//返回队列的队头元素
DataType QueueFront(Queue* pq)
{
	assert(pq);
	return pq->head->data;
}

//返回队列的队尾元素
DataType QueueBack(Queue* pq)
{
	assert(pq);
	return pq->tail->data;
}

//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	while (pq->head)
	{
		QNode* ret = pq->head;
		pq->head = pq->head->next;
		free(ret);
		ret = NULL;
	}
}


typedef struct {
    Queue s1;
    Queue s2;
} MyStack;

MyStack* myStackCreate() {
    MyStack*ret=(MyStack*)malloc(sizeof(MyStack));
    assert(ret);
    QueueInit(&(ret->s1));
    QueueInit(&(ret->s2));
    return ret;
}

void myStackPush(MyStack* obj, int x) 
{
    //当第二个队列为空时,向第一个队列当中插入数据
    if(QueueEmpty(&obj->s2))
    {
        QueuePush(&obj->s1,x);
    }
    //当第二个队列不为空的时候就向第一个队列当中插入数据
    else
    {
        QueuePush(&obj->s2,x);
    }

}

int myStackPop(MyStack* obj) {
    //将不为空的队列数据除了最后一个全部移到另一个队列当中
    if(QueueEmpty(&obj->s1))
    {
        //判断当前数据是否为最后一个要删除的数据
        while(QueueSize(&obj->s2)!=1)
        {
            DataType ret=QueueFront(&obj->s2);
            QueuePush(&obj->s1,ret);
            QueuePop(&obj->s2);
        }
        DataType tmp=QueueFront(&obj->s2);
        QueuePop(&obj->s2);
        return tmp;
    }
    else
    {
        while(QueueSize(&obj->s1)!=1)
        {
            DataType ret=QueueFront(&obj->s1);
            QueuePush(&obj->s2,ret);
            QueuePop(&obj->s1);
        }
        DataType tmp=QueueFront(&obj->s1);
        QueuePop(&obj->s1);
        return tmp;
    }
}

int myStackTop(MyStack* obj) 
{
    if(QueueEmpty(&obj->s1))
    {
        //判断当前数据是否为最后一个数据
        while(QueueSize(&obj->s2)!=1)
        {
            DataType ret=QueueFront(&obj->s2);
            QueuePush(&obj->s1,ret);
            QueuePop(&obj->s2);
        }
        DataType tmp=QueueFront(&obj->s2);
        QueuePush(&obj->s1,tmp);
        QueuePop(&obj->s2);
        return tmp;
    }
    else
    {
        while(QueueSize(&obj->s1)!=1)
        {
            DataType ret=QueueFront(&obj->s1);
            QueuePush(&obj->s2,ret);
            QueuePop(&obj->s1);
        }
        DataType tmp=QueueFront(&obj->s1);
        QueuePush(&obj->s2,tmp);
        QueuePop(&obj->s1);
        return tmp;
    }
}

bool myStackEmpty(MyStack* obj) 
{
    if(QueueEmpty(&obj->s1)&&QueueEmpty(&obj->s2))
    {
        return true;
    }
    return false;
}

void myStackFree(MyStack* obj) 
{
    //当队列1不为空时,释放队列1当中的所有元素
   while(!QueueEmpty(&obj->s1))
   {
       QueuePop(&obj->s1);
   }
   while(!QueueEmpty(&obj->s2))
   {
       QueuePop(&obj->s2);
   }
}

   我们使用队列实现栈的代码的逻辑如上所示。15e0152b6a544f87bb61af24dafd3b33.png

     🌵用栈实现队列

  使用队列实现完栈之后就轮到使用栈实现队列了。577310271c764e4f84b1912f95fd6007.png

  根据题目中的要求我们同样需要构建出两个栈以实现我们队列当中的功能。1c8d701e98c84c348de3a27906e9b614.png   我们的栈每次只能从栈顶插入数据从栈顶拿出数据,当我们想要以队列的形式拿出数据的时候,也就是从栈的底端拿出数据,我们就必须一个一个将数据全部搬运到另一个栈当中。我们剩下的最后一个数据也就是我们以队列的形式想要出的数据。d4396d51f2074507a23ab0236a2e6b75.png   我们会发现我们移入栈2当中的数据全部已经是排好顺序的了,那么我们每次出数据的时候只需要从栈2当中拿出数据得到的就是我们队头的数据。

  但是我们想要继续插入数据应该怎么办呢?如果直接插入栈2当中会是我们栈中元素顺序混乱,那么假如我们将全部数据都转入栈2当中呢?栈1就会为空,之后我们想要输入数据就向栈1当中输入,想要拿出数据据从栈2当中拿,当我们栈2当中的数据全部拿完时,我们再将栈1当中的数据全部转入栈2当中即可。

944965626e4d43b8b4324f03e976d4c0.png   重复上述步骤之后我们就可以使用栈实现一个队列了。具体的代码如下:

typedef int DataType;

typedef struct STstack
{
	DataType* data;
	int top;
	int capicity;
}ST;

//栈的初始化
void STInit(ST* s1)
{
	assert(s1);
	s1->data = (ST*)malloc(sizeof(ST)*4);
	s1->capicity = 4;
	s1->top = 0;
}

//判断是否需要扩容
void checkcapicity(ST* s1)
{
	if (s1->capicity > s1->top)
	{
		return;
	}
	else
	{
		//需要扩容进行扩容
		ST*tmp= (ST*)realloc(s1->data,sizeof(ST) * s1->capicity*2);
		if (tmp == NULL)
		{
			perror("malloc");
			return;
		}
		s1->data = tmp;
		s1->capicity *= 2;
	}
}

//入栈函数
void STPush(ST* s1, DataType x)
{
	assert(s1);
	//判断是否需要扩容
	checkcapicity(s1);
	s1->data[s1->top] = x;
	s1->top++;
}

//出栈函数
void STPop(ST* s1)
{
	assert(s1);
	//判断栈中是否存在数据,如果不存在数据就不可以进行删除
	if (s1->top == 0)
	{
		printf("空栈不允许进行删除。");
		return;
	}
	//如果不是空栈就删除栈顶元素
	s1->top--;
}

//返回栈顶元素
DataType STTop(ST* s1)
{
	assert(s1);
	//判断栈中是否存在数据
	assert(s1->top);
	return s1->data[s1->top-1];
}

//判断栈为空
bool STEmpty(ST* s1)
{
	assert(s1);
	if (s1->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

//返回链表的大小
int STSize(ST* s1)
{
	assert(s1);
	return s1->top;
}

//销毁栈
void STDestory(ST* s1)
{
	assert(s1);
	free(s1->data);
	s1->data == NULL;
	s1->top = 0;
	s1->capicity = 0;
}

typedef struct {
    ST s1;
    ST s2;
} MyQueue;


MyQueue* myQueueCreate() 
{
    MyQueue*ret=(MyQueue*)malloc(sizeof(MyQueue));
    assert(ret);
    STInit(&ret->s1);
    STInit(&ret->s2);
    return ret;
}

void myQueuePush(MyQueue* obj, int x) 
{
    //两个栈只在左边插入数据
    STPush(&obj->s1,x);
}

int myQueuePop(MyQueue* obj) 
{
    if(STEmpty(&obj->s2))
    {
        while(!STEmpty(&obj->s1))
        {
            //将左边栈中数据导入右栈当中
            DataType ret=STTop(&obj->s1);
            STPop(&obj->s1);
            STPush(&obj->s2,ret);
        }
    }
    DataType tmp=STTop(&obj->s2);
    STPop(&obj->s2);
    return tmp;
}

int myQueuePeek(MyQueue* obj) 
{
    if(STEmpty(&obj->s2))
    {
        while(!STEmpty(&obj->s1))
        {
            //将左边栈中数据导入右栈当中
            DataType ret=STTop(&obj->s1);
            STPop(&obj->s1);
            STPush(&obj->s2,ret);
        }
    }
    DataType tmp=STTop(&obj->s2);
    return tmp;
}

bool myQueueEmpty(MyQueue* obj) 
{
    if(STEmpty(&obj->s1)&&STEmpty(&obj->s2))
    {
        return true;
    }
    return false;
}

void myQueueFree(MyQueue* obj) 
{
    while(!STEmpty(&obj->s1))
    {
        STDestory(&obj->s1);
    }
    while(!STEmpty(&obj->s2))
    {
        STDestory(&obj->s2);
    }
}

   运行结果:

ea95e9661f1f43ed8e7ab7935c1505d3.png

  那么我们栈与队列的相互转换也就实现完毕了,本次博客的内容也就到此结束了。感谢您的观看,再见。 

 

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

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

相关文章

为生信写的Python简明教程 | 视频2

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在:https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

改进YOLOv8 | 即插即用篇 | CVPR2023最新注意力 | 《BiFormer:视觉变换器与双层路由注意力》

作为视觉变换器的核心构建模块,注意力是一种强大的工具,可以捕捉长程依赖关系。然而,这种强大的功能付出了代价:计算负担和内存占用巨大,因为需要在所有空间位置上计算成对的令牌交互。一系列的研究尝试通过引入手工制作和与内容无关的稀疏性来缓解这个问题,例如将注意力…

【技术分享】防止根据IP查域名,防止源站IP泄露

有的人设置了禁止 IP 访问网站,但是别人用 https://ip 的形式,会跳到你服务器所绑定的一个域名网站上 直接通过 https://IP, 访问网站,会出现“您的连接不是私密连接”,然后点高级,会出现“继续前往 IP”,…

81. read readline readlines 读取文件的三种方法

81. read readline readlines 读取文件的三种方法 文章目录 81. read readline readlines 读取文件的三种方法1. 读取文件的三种方法2. read方法3. readline方法4. readlines方法5. 代码总结5.1 read方法读取全部内容5.2 readline方法读取一行,返回字符串5.3 readli…

stable diffusion的使用

文章目录 1 文生图1.1 mountains and trees and gree1.2 three dogs1.3 cats1.4 three lovely cats1.5 beautiful girl1.6 机器猫1.7 卡通图像生成 1 文生图 1.1 mountains and trees and gree 1.2 three dogs 1.3 cats 1.4 three lovely cats 1.5 beautiful girl stable diff…

CRN Camera Radar Net for Accurate, Robust, Efficient 3D Perception

这是发表在ICLR2023 workshop on Scene Representations for Autonomous Driving上的文章,目前在nuScenes的cameraRadar的3D目标检测赛道上刷到了第一,非常值得一看 一、创新点和贡献 采用了一种两阶段的融合思路: 采用radar-assistant-vie…

【AI工具】bing chat 使用--三种模式+撰写功能

bing chat:三种模式撰写功能 以下为点击复制后粘贴的内容 Bing Chat提供三种对话模式可选择:创造力、平衡和精确。更多创造力(Creative):Bing Chat回答的内容将带有更多语气和情绪,更像一个真实的人类与用户对话。更多…

如何实现倾斜摄影三维模型数据裁剪和轻量化一体化处理?

如何实现倾斜摄影三维模型数据裁剪和轻量化一体化处理? 随着数字地球和数字城市的发展,倾斜摄影三维模型成为了重要的数据形式。然而,由于数据量大,处理难度大等问题,如何实现倾斜摄影三维模型数据裁剪和轻量化一体化处…

ChatGPT提示词工程(二):Iterative迭代

目录 一、说明二、安装环境三、Iterative第一次写Prompt第二次写Prompt第三次写Prompt第四次写Prompt第五次写Prompt 四、总结 一、说明 这是吴恩达 《ChatGPT Prompt Engineering for Developers》 的课程笔记系列。 本文是第三讲的内容:Iterative 课程主讲&#…

RabbitMQ 发布订阅模式,routing路由模式,topic模式

发布订阅模式 一个消息可以由多个消费者消费同一个消息 消费者1和2同时消费了该消息 举例 public static void main(String[] args) throws IOException, TimeoutException {//1 创建连接工厂ConnectionFactory connectionFactorynew ConnectionFactory();//2 设置rabbitmq …

机器学习与深度学习——通过knn算法分类鸢尾花数据集iris求出错误率并进行可视化

什么是knn算法? KNN算法是一种基于实例的机器学习算法,其全称为K-最近邻算法(K-Nearest Neighbors Algorithm)。它是一种简单但非常有效的分类和回归算法。 该算法的基本思想是:对于一个新的输入样本,通过…

Preftest测试

Preftest测试 Perftest 是一组基于uverbs编写的测试程序,是RDMA性能相关的micro-benchmark。可用于软硬件调优以及功能测试。 Perfest支持的测试 源码位置 linux-rdma/perftest: Infiniband Verbs Performance Tests (github.com) 安装 直接安装preftest sudo …

Flink作业任务的9种状态简单介绍

​ 当创建一个Flink任务后,该任务可能会经历多种状态。目前Flink给任务共定义了9种状态,包括:Created,Running,Finished,Cancelling,Canceled,Restarting,Failing&#x…

类加载器和双亲委派模型面试总结

类的生命周期和类加载的过程 在了解类加载器之前,我们先来了解一下一个类的生命周期和类加载的过程。 一个类完整的生命周期包括 加载、验证、准备、解析、初始化、使用和卸载,一共7个阶段。 类加载过程包括,加载、连接和初始化&#xff0…

腾讯云镜YunJing——Agent定时任务脚本分析

缘起 如果你有台腾讯云主机,会发现默认有个叫 YunJing 的进程。 把它kill掉后,发现一段时间又出现了 这是为什么捏? 分析定时任务配置文件 通过crontab定时任务目录, 会发现有个叫yunjing的配置文件。 */30 * * * * root /usr/local/qc…

PCIe物理层详细总结-PCIE专题知识(一)

目录 一、简介二、PCIe物理层结构及功能2.1 PCIe端对端连接方式2.2 PCIe组成2.2.1 逻辑层(Logic)1 发送逻辑2 接收逻辑 2.2.2 电气层(Electrical)1 物理层-电气(Physical Layer Electrical)2 数据传送----差分方式 2.2.3 PLP介绍 三、其他相关链接1、PCI总线及发展历程总结 一、…

ChatGPT 和 Elasticsearch:OpenAI 遇见私有数据(一)

作者:Jeff Vestal 结合 Elasticsearch 的搜索相关性和 OpenAI 的 ChatGPT 的问答功能来查询你的数据。 在此博客中,你将了解如何使用 Elasticsearch 将 ChatGPT 连接到专有数据存储,并为你的数据构建问答功能。 ChatGPT 和 Elasticsearch&…

【react从入门到精通】React JSX详解

文章目录 前言React技能树什么是JSXJSX的基本语法规则1.JSX必须有一个顶层元素2.JSX标签必须有一个结束标记,或者是自闭合的3.JSX中可以使用JavaScript表达式4.JSX中的样式和HTML中的样式有所不同5.JSX中的class属性必须写成className6.JSX中的style属性必须使用对象…

JavaWeb-HTML常用标签了解(一)

这里写目录标题 注释标签标题标签段落标签换行标签格式化标签图片标签超链接标签外部链接与内部链接 注释标签 <!-- 有注释 -->无注释ctrl / 快捷键可以快速进行注释/取消注释. 注意 注释不能传达负能量!!! 标题标签 有六个, 从 h1 - h6. 数字越大, 则字体越小. <h…

读SQL进阶教程笔记14_SQL编程要点

1. 消灭NULL 1.1. NULL惹人讨厌的原因 1.1.1. 进行SQL编码时&#xff0c;必须考虑违反人类直觉的三值逻辑 1.1.2. 指定IS NULL、IS NOT NULL的时候&#xff0c;不会用到索引&#xff0c;SQL语句执行起来性能低下 1.1.2.1. 1 NULL NULL2- NULL NULL3 &#xff0a; NULL …