用队列实现栈oj题——225

news2024/9/24 11:30:22

在这里插入图片描述在这里插入图片描述

.

个人主页:晓风飞
专栏:LeetCode刷题|数据结构|Linux
路漫漫其修远兮,吾将上下而求索


文章目录

  • 题目要求:
    • 实现 MyStack 类:
    • 注意:
    • 示例:
    • 解释:
    • 提示:
  • 解题核心
  • 数据结构的定义
  • 初始化栈
  • 入栈(Push)操作
  • 出栈(Pop)操作
  • 获取栈顶元素(Top):
  • 检查栈是否为空(Empty):
  • 销毁栈(Free):
  • 以下是队列的实现:
  • 以下是本题的实现:


要做题目的点击这里–>栈和队列oj题——225. 用队列实现栈

题目要求:

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

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

注意:

你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:
[“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

提示:

1 <= x <= 9
最多调用100 次 push、pop、top 和 empty
每次调用 pop 和 top 都保证栈不为空

进阶:你能否仅用一个队列来实现栈。
在这里插入图片描述
myStackCreate - 创建栈

解题核心

这个问题的核心思路在于使用两个队列(Queue)来模拟一个栈(Stack)的行为。栈是一种后进先出(LIFO, Last In First Out)的数据结构,而队列是一种先进先出(FIFO, First In First Out)的数据结构。要用队列模拟栈的行为,关键在于如何实现栈的两个主要操作:入栈(push)和出栈(pop)。

数据结构的定义

初始化两个队列,这两个队列将用于模拟栈的行为。

// 定义一个使用两个队列模拟的栈的结构体
typedef struct
{
    Queue q1; // 第一个队列
    Queue q2; // 第二个队列
} MyStack;

初始化栈

动态分配内存给新的栈,如果内存分配失败,输出错误信息并退出。

// 创建一个新的栈
MyStack* myStackCreate() 
{
    // 动态分配内存给新栈
    MyStack* newStack =(MyStack*)malloc(sizeof(MyStack));
    if (!newStack)
    {
        perror("malloc fail"); // 如果内存分配失败,输出错误信息并退出
        exit(-1);
    }

    // 初始化两个队列
    QInit(&(newStack->q1));
    QInit(&(newStack->q2));
    return newStack;  
}

入栈(Push)操作

在栈中,最新添加的元素总是被存储在栈的顶部。在使用两个队列模拟栈时,入栈操作相对直接:
选择一个非空队列进行操作:如果两个队列都是空的,可以选择任意一个队列进行操作。如果有一个非空队列,总是将新元素入队到这个非空队列。

// 将一个元素推入栈中
void myStackPush(MyStack* obj, int x) 
{
    assert(obj); // 确保栈对象非空
    // 总是将元素推入非空队列中
    if(!QueueEmpty(&obj->q1))
    {
        QPush(&obj->q1, x);
    }
    else
    {
        QPush(&obj->q2, x);
    }
}

出栈(Pop)操作

确定非空队列和空队列:首先识别出哪个队列是非空的(存有栈元素的队列),哪个队列是空的。

// 从栈中弹出一个元素
int myStackPop(MyStack* obj) 
{ 
    assert(obj); // 确保栈对象非空
    Queue* empty = &obj->q1; // 一个指向可能为空的队列的指针
    Queue* noEmpty = &obj->q2; // 一个指向非空队列的指针
    // 确定哪个队列是空的,哪个是非空的
    if(!QueueEmpty(empty))
    {
        empty = &obj->q2;
        noEmpty = &obj->q1;
    }
    // 将元素从非空队列转移到空队列,直到只剩下一个元素
    while(QueueSize(noEmpty) > 1)
    {
        QPush(empty, QueueFront(noEmpty));
        QPop(noEmpty);
    }
    // 弹出并返回最后一个元素
    int top = QueueFront(noEmpty);
    QPop(noEmpty);
    return top;
}

获取栈顶元素(Top):

栈顶元素对应于最后进入非空队列的元素。可以通过查看非空队列的尾部元素来得知栈顶元素。

// 获取栈顶元素
int myStackTop(MyStack* obj)
{
    assert(obj); // 确保栈对象非空
    // 返回非空队列的尾部元素(栈顶元素)
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

检查栈是否为空(Empty):

如果两个队列都为空,那么栈为空。

// 检查栈是否为空
bool myStackEmpty(MyStack* obj) 
{  
    assert(obj); // 确保栈对象非空
    // 如果两个队列都为空,栈为空
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

销毁栈(Free):

释放栈所使用的资源,包括两个队列和栈本身的内存。

// 释放栈所占用的资源
void myStackFree(MyStack* obj) 
{
    assert(obj); // 确保栈对象非空
    // 销毁两个队列
    QDestroy(&obj->q1);
    QDestroy(&obj->q2);
    // 释放栈对象所占用的内存
    free(obj);
}

以下是队列的实现:

typedef int QDataType; // 定义队列数据类型为int

// 队列节点的结构体定义
typedef struct QueueNode
{
  QDataType val; // 节点存储的数据
  struct QueueNode* next; // 指向下一个节点的指针
} QNode;

// 队列的结构体定义
typedef struct Queue
{
  QNode* phead; // 指向队列头部的指针
  QNode* ptail; // 指向队列尾部的指针
  int size; // 队列的大小
} Queue;

// 函数声明
void QInit(Queue* pq); // 初始化队列
void QDestroy(Queue* pq); // 销毁队列

void QPush(Queue* pq, QDataType x); // 向队列中添加元素
void QPop(Queue* pq); // 从队列中移除元素

QDataType QueueFront(Queue* pq); // 获取队列头部元素
QDataType QueueBack(Queue* pq); // 获取队列尾部元素

bool QueueEmpty(Queue* pq); // 检查队列是否为空
int QueueSize(Queue* pq); // 获取队列的大小

// 初始化队列
void QInit(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  pq->phead = pq->ptail = NULL; // 将头指针和尾指针都设为NULL
  pq->size = 0; // 将队列大小设置为0
}

// 销毁队列
void QDestroy(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  QNode* cur = pq->phead;
  while (cur)
  {
    QNode* next = cur->next; // 保存下一个节点
    free(cur); // 释放当前节点
    cur = next; // 移动到下一个节点
  }
  pq->phead = NULL; // 将头指针设为NULL
  pq->ptail = NULL; // 将尾指针设为NULL
  pq->size = 0; // 将队列大小设置为0
}

// 向队列中添加元素
void QPush(Queue* pq, QDataType x)
{
  assert(pq); // 断言队列指针非空
  QNode* newNode = (QNode*)malloc(sizeof(QNode)); // 分配新节点内存
  if (newNode == NULL)
  {
    perror("malloc fail"); // 内存分配失败处理
    exit(-1);
  }

  newNode->val = x; // 设置新节点的值
  newNode->next = NULL; // 新节点的下一个节点为NULL
  if (pq->ptail == NULL) // 如果队列为空
  {
    pq->phead = pq->ptail = newNode; // 队列头尾都指向新节点
  }
  else
  {
    pq->ptail->next = newNode; // 将新节点接到队列尾部
    pq->ptail = newNode; // 更新尾指针
  }
  pq->size++; // 队列大小增加
}

// 从队列中移除元素
void QPop(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  assert(pq->phead); // 断言队列不为空
  QNode* Del = pq->phead; // 保存要删除的节点
  pq->phead = pq->phead->next; // 更新头指针
  free(Del); // 释放节点内存
  if (pq->phead == NULL) // 如果队列变空
  {
    pq->ptail = NULL; // 更新尾指针
  }
  pq->size--; // 队列大小减少
}

// 获取队列头部元素的值
QDataType QueueFront(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  assert(pq->phead); // 断言队列不为空
  return pq->phead->val; // 返回头部元素的值
}

// 获取队列尾部元素的值
QDataType QueueBack(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  assert(pq->ptail); // 断言队列不为空
  return pq->ptail->val; // 返回尾部元素的值
}

// 检查队列是否为空
bool QueueEmpty(Queue* pq)
{
  assert(pq); // 断言队列指针非空
  return pq->phead == NULL; // 如果头指针为NULL,则队列为空
}

// 获取队列的大小
int QueueSize(Queue* pq)
{
  return pq->size; // 返回队列的大小
}

以下是本题的实现:

// 定义一个使用两个队列模拟的栈的结构体
typedef struct
{
    Queue q1; // 第一个队列
    Queue q2; // 第二个队列
} MyStack;

// 创建一个新的栈
MyStack* myStackCreate() 
{
    // 动态分配内存给新栈
    MyStack* newStack =(MyStack*)malloc(sizeof(MyStack));
    if (!newStack)
    {
        perror("malloc fail"); // 如果内存分配失败,输出错误信息并退出
        exit(-1);
    }

    // 初始化两个队列
    QInit(&(newStack->q1));
    QInit(&(newStack->q2));
    return newStack;  
}

// 将一个元素推入栈中
void myStackPush(MyStack* obj, int x) 
{
    assert(obj); // 确保栈对象非空
    // 总是将元素推入非空队列中
    if(!QueueEmpty(&obj->q1))
    {
        QPush(&obj->q1, x);
    }
    else
    {
        QPush(&obj->q2, x);
    }
}

// 从栈中弹出一个元素
int myStackPop(MyStack* obj) 
{ 
    assert(obj); // 确保栈对象非空
    Queue* empty = &obj->q1; // 一个指向可能为空的队列的指针
    Queue* noEmpty = &obj->q2; // 一个指向非空队列的指针
    // 确定哪个队列是空的,哪个是非空的
    if(!QueueEmpty(empty))
    {
        empty = &obj->q2;
        noEmpty = &obj->q1;
    }
    // 将元素从非空队列转移到空队列,直到只剩下一个元素
    while(QueueSize(noEmpty) > 1)
    {
        QPush(empty, QueueFront(noEmpty));
        QPop(noEmpty);
    }
    // 弹出并返回最后一个元素
    int top = QueueFront(noEmpty);
    QPop(noEmpty);
    return top;
}

// 获取栈顶元素
int myStackTop(MyStack* obj)
{
    assert(obj); // 确保栈对象非空
    // 返回非空队列的尾部元素(栈顶元素)
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

// 检查栈是否为空
bool myStackEmpty(MyStack* obj) 
{  
    assert(obj); // 确保栈对象非空
    // 如果两个队列都为空,栈为空
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

// 释放栈所占用的资源
void myStackFree(MyStack* obj) 
{
    assert(obj); // 确保栈对象非空
    // 销毁两个队列
    QDestroy(&obj->q1);
    QDestroy(&obj->q2);
    // 释放栈对象所占用的内存
    free(obj);
}

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

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

相关文章

通信触发流程

该示例方案主要介绍如何通过建立的Modbus或TCP通信来实现触发方案、协议解析、发送事件和以及响应配置等功能。 需求&#xff1a;使用Modbus通信触发指定流程运行。 搭建思路&#xff1a;在接收事件中使用协议组装&#xff0c;比较规则选择上升沿&#xff0c;当接收到的值从其…

如何成为一名高级UI设计师,UI设计与交互动效进阶教学

一、教程描述 本套教程从基础学习到案例实践教学&#xff0c;涵盖了设计师所需的全部知识和实战技术&#xff0c;比如&#xff0c;品牌形象设计&#xff0c;企业视觉识别系统VIS&#xff0c;标志logo提案设计&#xff0c;banner运营视觉设计&#xff0c;字体搭配使用&#xff…

学习笔记 | Kafka

一、概述 定义 1、Kafka传统定义&#xff1a;Kafka 是一个分布式的基于 发布/订阅模式 的消息队列&#xff08;Message Queue&#xff09; &#xff0c;主要应用与大数据实时处理领域。 2、发布/订阅&#xff1a;消息的发送者不会将消息直接发送给特定的订阅者&#xff0c;而…

企业网盘全方位解读:热门云存储工具的优势与适用场景

企业网盘无疑是当下最热门的企业协同工具。什么是企业网盘&#xff1f;企业网盘与个人网盘又有什么不同呢&#xff1f;一文全方位解读企业网盘这一热门云存储工具。 什么是企业网盘 企业网盘为企业级文件存储、管理与共享平台&#xff0c;企业团队可以在企业网盘中存储企业文…

【python】连上钉钉机器人每日推送

使用Python向钉钉机器人发送消息 导入必要的库 导入json库用于处理JSON数据&#xff0c;time库用于获取当前时间&#xff0c;requests库用于发送HTTP请求。 定义send_ding_message函数 该函数接收一个消息作为参数&#xff0c;并通过POST请求发送给钉钉机器人。请求的URL和头部…

阿里云服务器可用区是什么?

阿里云服务器地域和可用区怎么选择&#xff1f;地域是指云服务器所在物理数据中心的位置&#xff0c;地域选择就近选择&#xff0c;访客距离地域所在城市越近网络延迟越低&#xff0c;速度就越快&#xff1b;可用区是指同一个地域下&#xff0c;网络和电力相互独立的区域&#…

JS加密/解密之常见的JS代码加密

在软件开发领域&#xff0c;混淆&#xff08;JS&#xff09;是一种常见的技术&#xff0c;通过改变代码结构、命名和逻辑&#xff0c;增加代码的复杂性&#xff0c;使其对于逆向工程者变得更加困难。然而&#xff0c;有时候开发者可能需要解开混淆&#xff0c;以便理解、维护或…

jupyter如何更改默认保存路径

jupyter更改默认路径 jupyter默认路径在‘c\用户\Administrator’下&#xff0c;很不方便。 接下来看如何更改默认路径: 1、找到Anaconda Prompt,打开 2、 输入conda activate env1&#xff0c;其中env1为自己创建的环境&#xff0c;如果不知道怎么创建&#xff0c;按照下面…

技术学习周刊第 1 期

2018 年参与过 1 年的 ARTS 打卡&#xff0c;也因为打卡有幸加入了 MegaEase 能与皓哥&#xff08;左耳朵耗子&#xff09;共事。时过境迁&#xff0c;皓哥已经不在了&#xff0c;自己的学习梳理习惯也荒废了一段时间。 2024 年没给自己定具体的目标&#xff0c;只要求自己好好…

大模型迎来“AppStore时刻”,OpenAI给2024的新想象

一夜之间&#xff0c;OpenAI公布了多个重磅消息&#xff0c;引发市场关注。 钛媒体App 1月5日消息&#xff0c;今晨&#xff0c;OpenAI公司向所有GPT开发者们发布一封邮件称&#xff0c;下周将上线自定义的“GPT Store”商店&#xff0c;这有望推动ChatGPT开发者生态不断完善。…

java基础-给个一键三联呗^_^哈哈

文章目录 1.注释修改注释字体三种注释方式 2.标识符和关键字3.数据类型4.类型转换5. 变量、常量、作用域6.基本运算符7.自增自减运算符、初识Math类8.逻辑运算符、位运算符9.三元运算符及小结10.包机制11.JavaDoc生成文档 1.注释 修改注释字体 打开设置Settings 三种注释方…

IDEA 每次新建工程都要重新配置 Maven的解决方案

文章目录 IDEA 每次新建工程都要重新配置 Maven 解决方案一、选择 File -> New Projects Setup -> Settingsfor New Projects…二、选择 Build,Execution,Deployment -> Build Tools -> Maven IDEA 每次新建工程都要重新配置 Maven 解决方案 DEA 每次新建工程都要…

lv14 ioctl、printk及多个此设备支持 6

1 ioctl操作实现 对相应设备做指定的控制操作&#xff08;各种属性的设置获取等等&#xff09; long xxx_ioctl (struct file *filp, unsigned int cmd, unsigned long arg); 功能&#xff1a;对相应设备做指定的控制操作&#xff08;各种属性的设置获取等等&#xff09; 参数…

关于vite的glob坑

我先展示一段代码&#xff1a; /*** function 根据pages路径动态生成路由* param {Array} 基础路由*/ export default function (routes) {const modules import.meta.glob("../pages/**/page.js", { eager: true, import: "default" });const comps im…

CSS3渐变属性详解

渐变属性 线性渐变 概念&#xff1a;线性渐变&#xff0c;指的是在一条直线上进行的渐变。在线性渐变过程中&#xff0c;起始颜色会沿着一条直线按顺序过渡到结束颜色 语法&#xff1a; background:linear-gradient(渐变角度&#xff0c;开始颜色&#xff0c;结束颜色);渐变…

循环队列的队空队满情况

有题目&#xff1a; 循环队列放在一维数组A[0....M-1]中&#xff0c;end1指向队头元素&#xff0c;end2指向队尾元素的后一个位置。假设队列两端均可进行入队和出队操作&#xff0c;队列中最多能容纳M-1个元素。初始时为空。下列判断队空和队满的条件中&#xff0c;正确的是 …

移动通信原理与关键技术学习(第四代蜂窝移动通信系统)

前言&#xff1a;LTE 标准于2008 年底完成了第一个版本3GPP Release 8的制定工作。另一方面&#xff0c;ITU 于2007 年召开了世界无线电会议WRC07&#xff0c;开始了B3G 频谱的分配&#xff0c;并于2008 年完成了IMT-2000&#xff08;即3G&#xff09;系统的演进——IMT-Advanc…

进程与计划任务管理

目录 一、进程 1.进程相关概念 2.判断线程 3.进程的命令 ps命令 top命令 pstree命令 kill与killall命令 二、计划任务 1.一次性执行任务 2.定时性周期任务 一、进程 1.进程相关概念 程序&#xff1a;保存在硬盘等介质中的可执行的代码。 进程&#xff1a;正在运行…

C++学习笔记(二十四):c++ this

this指针在c中较为常用。this是一个指向当前对象实例的指针&#xff0c;通过this指针&#xff0c;可以访问该类的成员函数。示例如下&#xff1a;this指针主要的使用场景是在类内部调用类外部的函数&#xff0c;该函数传递的参数是调用该函数的类对象&#xff0c;代码示例如下&…

关于整形提升

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 什么是整型提升&#xff1f; 在C语言的整型算数运算总是至少以int类型来进行的&#xff0c;当表达式中有char&#xff0c;byte&#xff0c;short类型的操作数时&#xff0c;他们在被使用前需要被转换成int类型&#xff0c;…