队列实现栈(你看我讲的是不是最细的就完了)

news2024/12/25 9:25:21

最伟大的成就往往起源于最强烈的热情。                   -- 诺曼·文森特·皮尔
目录

🗼一.队列实现栈

🍅二.使用两个队列来模拟实现栈

🍋1.栈结构体包含两个队列 

🍒2.创建一个结构体的指针 

🍂3.myStackPush入栈操作

🌺4.myStackPop出队列操作并返回剩下的一个元素,也就是栈顶的元素

🍁5.myStackTop返回栈顶的元素

☘️6.myStackEmpty判断空

🌳7. myStackFree释放模拟的栈

🍑三.完整代码



题目描述:
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。
实现 MyStack 类:

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

 

示例:

输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

注意:这里myStack.pop()和myStack.top()都是先保存栈顶的元素,然后再返回栈顶的元素,只是myStack.pop()还要删除栈顶的元素。所以上述的解释为啥这两个函数返回的都是2,就是这个原因。一定要理解上述的MyStack类的函数,才能后续更好的写代码。
还有一个队列实现栈的,大家可以自行下去试试手。
做题链接:队列实现栈

🗼一.队列实现栈

🍅二.使用两个队列来模拟实现栈

思路讲解:我们使用两个队列来回倒数据,利用队列先进先出的性质,每次等一个队列往另一个队列倒数据还剩下一个时,这便是队列的尾,也就是栈的头了。不清楚?没关系啊,老样子画图理解:

创建好两个栈了,关键就是如何实现栈的功能? 

这时我们把队列1的前4个数据往队列2里面入数据,然后剩下的5就是栈顶的元素了,直接pop掉即可,因为这符合栈的数据后进先出的规则,出的数据就是队列1剩下的5。

思路还是比较简单的,具体就要看如何写代码: 
先大致看一下接口函数:

typedef struct {

} MyStack;


MyStack* myStackCreate() {

}

void myStackPush(MyStack* obj, int x) {

}

int myStackPop(MyStack* obj) {

}

int myStackTop(MyStack* obj) {

}

bool myStackEmpty(MyStack* obj) {

}

void myStackFree(MyStack* obj) {

}

🍋1.栈结构体包含两个队列 

typedef struct {//栈结构体包含两个队列
  Queue q1;//队列1
  Queue q2;//队列2
} MyStack;

🍒2.创建一个结构体的指针 

平常我们写栈和队列的时候,都是定义一个结构体的变量,然后使用传结构体变量的地址,也就是传参的方式,但是今天这个接口函数是使用的返回值的形式,我们要注意。

MyStack* myStackCreate() 
{
  MyStack*obj=(MyStack*)malloc(sizeof(MyStack));//创建一个结构体的指针
  if(obj==NULL)
  return NULL;
  QueueInit(&obj->q1);//初始化队列1
  QueueInit(&obj->q2);//初始化队列2
  return obj;//返回结构体的指针
}

🍂3.myStackPush入栈操作

入栈开始我们画图就说了,也就是这里的入队列,第一次两个队列都为空,然后随便往哪个队列入数据即可。

void myStackPush(MyStack* obj, int x) {
   if(!QueueEmpty(&obj->q1))
   //这里是队列1尾非空,那么就是队列1有数据了,那就继续入数据
   {
        QueuePush(&obj->q1,x);
   }
   else//这里就是当队列1为空时,那么队列2为空或者不为空,那么都往队列2入数据
   {
        QueuePush(&obj->q2,x);
   }
}

🌺4.myStackPop出队列操作并返回剩下的一个元素,也就是栈顶的元素

这个函数就是开始倒数据了,把一个队列的数据倒到另一个队列里面去,然后队列还剩下一个没倒过去的数据,这个就是栈顶的元素,返回栈顶的元素,然后删除栈顶的元素。

int myStackPop(MyStack* obj)
{
    Queue*pEmpty=&obj->q1;//这时我们不知道那个队列是空的,这里我们假设队列1为空
    Queue*pNonEmpty=&obj->q2;
    if(!QueueEmpty(pEmpty))
    {
        pEmpty=&obj->q2;//如果我们假设错误,就交换一下即可
        pNonEmpty=&obj->q1;
    }
       while(QueueSize(pNonEmpty)>1)//QueueSize是队列里面的函数,求此时队列元素的个数
       {                        //这里因为我们需要返回剩下的一个元素,所以循环条件为大于1
          QueuePush(pEmpty,QueueFront(pNonEmpty));//把队列头的元素依次往另一个队列里面倒
          QueuePop(pNonEmpty);//然后依次pop出这个队列的元素
       }
       int top=QueueFront(pNonEmpty);//这就是剩下的那个元素
       QueuePop(pNonEmpty);
       return top;
}

🍁5.myStackTop返回栈顶的元素

这个实现就非常简单了,上面的pop函数其实差不多都已经实现了。

int myStackTop(MyStack* obj)
{
     if(!QueueEmpty(&obj->q1))//如果队列1不为空,返回队列尾的元素
       return QueueBack(&obj->q1);//也就是剩下的那个元素,也就是栈顶的元素
     else
       return QueueBack(&obj->q2);
}

☘️6.myStackEmpty判断空

bool myStackEmpty(MyStack* obj) 
{
  return QueueEmpty(&obj->q1)//两个队列都为空时,栈也就为空了,所以使用逻辑符且
     &&QueueEmpty(&obj->q2);
}

🌳7. myStackFree释放模拟的栈

void myStackFree(MyStack* obj) 
{
  QueueDestroy(&obj->q1);//销毁队列1和2
  QueueDestroy(&obj->q2);
  free(obj);//再释放栈的结构体指针
}

🍑三.完整代码

typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* front;
	QNode* rear;
	int size;
}Queue;

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType x);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);

// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

void QueueInit(Queue* q)
{
	assert(q);
	q->front=NULL;
    q->rear = NULL;
    q->size=0;
}

// 队尾入队列 
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc\n");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	//当只要一个结点
	if (q->rear == NULL)
	{
		q->rear = q->front = newnode;
	}
	//当有两个结点的时候
	else
	{
		q->rear->next = newnode;
		q->rear = newnode;
	}
	q->size++;
}
// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->front);
	//当只要一个结点时
	if (q->front->next == NULL)
	{
		free(q->front);
		q->front = q->rear = NULL;
	}
	else//当有两个及两个以上的结点的时候
	{
		Queue* next = q->front->next;
		free(q->front);
		q->front = NULL;
		q->front = next;
	}
	q->size--;
}

// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->front->data;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->front == NULL
		&& q->rear == NULL;
}

// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		Queue* next = cur->next;
		free(cur);
		cur = NULL;
		cur = next;
	}
	q->front = q->rear = NULL;
}

// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->rear);
	return q->rear->data;
}

typedef struct {//栈结构体包含两个队列
  Queue q1;//队列1
  Queue q2;//队列2
} MyStack;


MyStack* myStackCreate() 
{
  MyStack*obj=(MyStack*)malloc(sizeof(MyStack));//创建一个结构体的指针
  if(obj==NULL)
  return NULL;
  QueueInit(&obj->q1);//初始化队列1
  QueueInit(&obj->q2);//初始化队列2
  return obj;//返回结构体的指针
}

void myStackPush(MyStack* obj, int x) {
   if(!QueueEmpty(&obj->q1))
   //这里是队列1尾非空,那么就是队列1有数据了,那就继续入数据
   {
        QueuePush(&obj->q1,x);
   }
   else//这里就是当队列1为空时,那么队列2为空或者不为空,那么都往队列2入数据
   {
        QueuePush(&obj->q2,x);
   }
}

int myStackPop(MyStack* obj)
{
    Queue*pEmpty=&obj->q1;//这时我们不知道那个队列是空的,这里我们假设队列1为空
    Queue*pNonEmpty=&obj->q2;
    if(!QueueEmpty(pEmpty))
    {
        pEmpty=&obj->q2;//如果我们假设错误,就交换一下即可
        pNonEmpty=&obj->q1;
    }
       while(QueueSize(pNonEmpty)>1)//QueueSize是队列里面的函数,求此时队列元素的个数
       {                            //这里因为我们需要返回剩下的一个元素,所以循环条件为大于1
           QueuePush(pEmpty,QueueFront(pNonEmpty));//把队列头的元素依次往另一个队列里面倒
           QueuePop(pNonEmpty);//然后依次pop出这个队列的元素
       }
       int top=QueueFront(pNonEmpty);//这就是剩下的那个元素
       QueuePop(pNonEmpty);
       return top;
}

int myStackTop(MyStack* obj)
{
     if(!QueueEmpty(&obj->q1))//如果队列1不为空,返回队列尾的元素
       return QueueBack(&obj->q1);//也就是剩下的那个元素,也就是栈顶的元素
     else
       return QueueBack(&obj->q2);
}

bool myStackEmpty(MyStack* obj) 
{
  return QueueEmpty(&obj->q1)//两个队列都为空时,栈也就为空了,所以使用逻辑符且
     &&QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) 
{
  QueueDestroy(&obj->q1);//销毁队列1和2
  QueueDestroy(&obj->q2);
  free(obj);//再释放栈的结构体指针
}

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

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

相关文章

【小沐学Python】Python实现Web图表功能(ECharts.js,Flask)

🎈🎈🎈Python实现Web图表功能系列:🎈🎈🎈1🎈【Web开发】Python实现Web图表功能(D-Tale入门)🎈2🎈【Web开发】Python实现Web图表功能&a…

编程不头秃,Google「AI程序员」来了,聊天就能敲代码

上周 Google 在 I/O 大会宣布了一个能够辅助编程的聊天机器人 Codey,现在它终于上线 Google Colab 啦! 🌟 Codey 是基于 Google 目前最新的大语言模型 PaLM 2 运行,有着强大的语言理解和编程能力。 Codey 有这些功能&#xff1…

【k8s】【ELK】【三】Sidecar容器运行日志Agent

1、日志收集场景分析与说明 对于那些能够将日志输出到本地文件的Pod,我们可以使用Sidecar模式方式运行一个日志采集Agent,对其进行单独收集日志1、首先需要将Pod中的业务容器日志输出至本地文件,而后运行一个Filebeat边车容器,采…

chatgpt赋能Python-python3_绝对值

Python3中的绝对值 在本文中,我们将深入了解Python3中的绝对值(Absolute Value)以及如何在Python3中使用它。 我将介绍Python3的abs函数,它是一个内置函数,用于计算数字的绝对值。 什么是绝对值? 在数学…

详解MySQL主从复制

目录 1.概述 2.配置使用 2.1.master配置 2.2.slave配置 2.3.认主 2.4.确认认主结果 3.请求分发 3.1.概述 3.2.手动分发 3.2.1.原生JDBC 3.2.2.数据源 3.2.3.中间件 1.概述 在实际的数据密集型应用中,数据库层面往往呈现两个特点: 单点数据…

VONR排查指导分享

不能注册或呼叫到SIP服务器端30秒挂断呼叫的黄金法则咬线或摘机状态单通或无语音收到400 bad request收到413,513 Request Entity Too Large或Message Too Large消息收到408, 480或者487 消息483 - Too Many Hops488 – Not Acceptable Here语音质量和思…

chatgpt赋能Python-python3_9怎么安装

Python 3.9:安装指南 如果你正在学习编程或者已经是一名程序员,那么一定会了解到Python这个编程语言。Python是一种高级编程语言,其强大的设计特点和易于操作的特性使其成为了开发人员的首选。Python 3.9已经发布了,它虽然不是Py…

CSDN官方创作助手InsCode AI 教你分分钟搞定一篇好文章

CSDN官方推出创作助手InsCode AI很多天了,有心人都能发现,在写作界面的右上角多了一个创作助手的浮动按钮,点击后出现如下界面: 现阶段是“限免”状态,不好好利用它来创作,就有点辜负CSDN官方大佬们的良苦用…

【王道·计算机网络】第五章 传输层【未完】

一、传输层概述 传输层为应用层提供通信服务,使用网络层服务传输层的功能: 提供进程和进程之间的逻辑通信(网络层提供主机之间的逻辑通信)复用(发送发不同的应用进程)和分用(接收方正确的数据传…

Primer C++(第三章)

补码、原码和反码 正数的原码、反码、补码都相同 负数的补码:1、正数的原码符号位由0变1 (负数的原码) 2、对负数的原码除符号位外每位取反 (负数的反码) 3、对负数的反码末尾1 (负数的补码) …

众位力量汇集《永恒之塔私服》新版龙战前传

盛大游戏《永恒之塔》从万众翘首企盼中登陆国服到现在,已经过去了一年有余。在前不久前更新的周年庆版本“云上的召唤”中,精灵星的宝宝终于可以和精灵星一起翱翔在天际了…… “云上的召唤”我们还没有体验够,全新版本“龙战前传”已然于7月…

【观察】从业界首款“空间穿越屏”,看华为全屋智能的进化与重构

这个时代,“家”的构成不再是简单的一家三口,客厅、厨房、卧室也不再只是承担某个单一功能或场景的空间。 无数身在异乡打拼的青年,开始向往一个专属的独立空间;那些奔波劳碌的中年夫妻,在为家人创造更好生活环境的同时…

RabbitMQ_面试题01

文章目录 1.RabbitMQ如何防止消息堆积2.RabbitMQ如何保证消息顺序消费3.RabbitMQ如何防止消息重复消费4.RabbitMQ如何保证消息可靠性4.1 消息持久化4.2 生产者确认2.2.1 application.yml2.2.2 Config2.2.3 Test 4.3 消费者确认4.3.1 application.yml4.3.2 Test 1.RabbitMQ如何防…

OPT (奥普特)锂电池视觉检测技术精彩亮相CIBF

5月16~18日,第十五届中国国际电池技术展览会在深圳举办,全球2500多家优秀电池企业参展。 OPT(奥普特)作为锂电行业机器视觉核心供应商,携3D、深度学习、分频技术等视觉检测技术亮相,并展示了上…

chatgpt赋能Python-python3免费吗

Python3免费吗? Python3到底免费还是收费呢?这是一个被许多人关注和疑惑的问题。本文将从不同方面解答这个问题,希望能给你提供一个清晰的认识。 什么是Python3? Python3是一种通用、高级、解释型的编程语言。它是由Guido van …

【Linux初阶】fork进程创建 进程终止 进程等待

🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux初阶】 ✒️✒️本篇内容:fork进程创建,理解fork返回值和常规用法,进程终止(退出码、退出场景、退出…

第08章_聚合函数

第08章_聚合函数 我们上一章讲到了 SQL 单行函数。实际上 SQL 函数还有一类,叫做聚合(或聚集、分组)函数,它是对一组数据进行汇总的函数,输入的是一组数据的集合,输出的是单个值。 1. 聚合函数介绍 什么是…

【sentinel】Sentinel工作主流程以流控规则源码分析

Sentinel工作主流程 在Sentinel里面,所有的资源都对应一个资源名称(resourceName),每次资源调用都会创建一个Entry对象。Entry可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用SphU API显式创建。Entry创…

跨境seo引流的13种方法

跨境SEO引流是一种通过搜索引擎优化来吸引国际目标受众并增加网站流量的策略。以下是一些跨境SEO引流的关键步骤和技巧: 目标受众研究:了解你的目标市场和受众群体。了解他们的需求、喜好、购买习惯以及使用的搜索引擎和关键词。这将帮助你确定你的跨境S…

chatgpt赋能Python-python3怎么合并列表

Python3:合并列表的不同方法 如果你正在使用Python 3,那么你很可能会面对合并列表的问题。合并列表(也称为连接列表或串联列表)是将两个或多个列表组合成一个列表的过程,这是在编程中很常见的任务。在这篇文章里&…