当前位置: 首页 > article >正文

【初阶解法-数据结构】包含min函数的栈(代码+图示)

【数据结构】刷题-包含min函数的栈(代码+图示)-初阶解法

文章目录

  • 【数据结构】刷题-包含min函数的栈(代码+图示)-初阶解法
    • 题目
    • 提炼题目要求
    • 分析题目
    • 总结思路
    • 代码
    • 时间/空间复杂度
    • 进阶版

题目

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。

此栈包含的方法有:

push(value):将value压入栈中

pop():弹出栈顶元素

top():获取栈顶元素

min():获取栈中最小元素

数据范围:操作数量满足 0≤n≤300 ,输入的元素满足 ∣val∣≤10000

栈的各个操作的时间复杂度是 O(1) ,空间复杂度是O*(n)

提炼题目要求

题目核心要求

  • 实现push(value),pop(),top(),min()函数
  • 要求各个函数的时间复杂度均为O(1)
  • 空间复杂度为O(n)

分析题目

解决这类问题的关键是设计一个能够在O(1)时间内完成 pushpoptopmin 操作的数据结构。通常,我们会考虑 使用辅助栈或其他数据结构来维护额外的信息。

当我遇到这类问题时,我会考虑以下几个步骤:

  1. 使用辅助栈:
    • 我通常会考虑使用一个辅助栈来保存额外的信息,比如最小值。
    • 辅助栈可以在主栈的每个状态下都保存一些额外的信息,以便在O(1)时间内支持 min 操作。
  2. 考虑特殊情况:
    • 考虑空栈的情况,并确保代码对空栈的处理是正确的。
  3. 保持同步:
    • 在使用辅助栈或其他数据结构时,确保主栈和辅助数据结构之间保持同步,以防止出现不一致的情况。
  4. 复杂度分析:
    • 在实现解决方案后,分析代码的时间复杂度和空间复杂度,确保它们满足问题的要求。

举例来说,如果题目要求实现一个支持O(1)时间复杂度的 min 操作的栈,我可能会考虑使用辅助栈来保存每个状态下的最小值。在 push 操作中,我会在辅助栈中更新最小值;在 pop 操作中,我会同时弹出主栈和辅助栈的栈顶元素。这样就能够保持O(1)时间复杂度的 min 操作。


总结思路

下面是一种使用辅助栈的简单实现思路初阶

  1. 主栈 s1 用于正常的栈操作。
  2. 辅助栈 s2 用于存储每个状态下的最小值。

push 操作时:

  • 将元素压入主栈 s1
  • 检查辅助栈 s2 是否为空,或者新元素是否小于等于辅助栈的栈顶元素。如果是,将新元素也压入辅助栈 s2。否则,重复压入辅助栈的栈顶元素。

pop 操作时:

  • 分别从主栈 s1 和辅助栈 s2 弹出栈顶元素。

min 操作时:

  • 直接返回辅助栈 s2 的栈顶元素。

NOTE : 这样,辅助栈 s2 中的栈顶元素始终对应于主栈 s1 的当前最小值,而且由于每次 pushpop 操作都会同时更新两个栈,保持了两个栈的同步。这样,min 操作的时间复杂度是 O(1)。

如果对于push操作中的重复压入辅助栈的栈顶元素感到困惑,我将详细解释这背后的原因。

  • 当我们仅仅在push操作中实时监控辅助栈s2的栈顶元素是否一直保持是所有元素中的最小值时,这只是解决了问题的一半。因为栈中元素的变动不仅仅是由push操作引起的,我们还需要考虑pop操作。在弹出元素的同时,主栈s1中的元素也发生了变化。因此,为了确保s2能够实时监控到pop操作,我们必须同时在主栈s1进行pop操作的时候,保证辅助栈s2也要同时更新以进行pop操作。那么问题来了,如何在s2中保证有元素可以弹出,并且不会改变栈顶元素一直是最小值呢?
    • 解决这个问题的方法是,在入栈push的时候,如果待插入的元素比最小值大,我们就将当前最小值重复入栈。这样就保证了每当主栈s1入栈一个元素时,辅助栈s2也会入栈一个元素。这种做法的好处在于,之后无论何时进行出栈pop操作,我们都能够确保s2中有元素可以弹出。而且,一旦成功弹出元素,s2的栈顶元素仍然保持是最小值!
    • 如下图演示

image-20231204215629193

(同步)正常的出栈操作

image-20231204215913351

(未同步)异常的出栈操作

image-20231204220235603

代码

import java.util.*;
import java.util.Stack;


/*
含有min函数的栈

这个题目需要我们自己实现栈的基本的结构
 */

public class Solution{

    //这个栈1是我们的主要的栈
    Stack<Integer> stack1 = new Stack<>();

//这个栈2 是我们专门用来保存最小值的栈
    Stack<Integer> stack2 = new Stack<>();

    public void push(int node) {

        //准备一个辅助栈,这个栈必须在push方法中,因为这样我们就可以直接就保证min函数里面的栈中保存的一定是最小的值
        //所以我们就只需要在插入元素的时候就进行筛查
        stack1.push(node);
       
        if(stack2.empty()||node<stack2.peek()){
            stack2.push(node);
        }else{
            stack2.push(stack2.peek());
        }
    }

    public void pop() {
        stack1.pop();
        stack2.pop();
    }

    public int top() {
        return stack1.peek();

    }

    public int min() {
        return stack2.peek();
    }
}

时间/空间复杂度


时间复杂度分析

push(int node):

  • stack1.push(node):将元素推入主栈 stack1,O(1)。
  • 判断并更新最小值:
    • 如果 stack2 为空,或者新元素 node 小于 stack2 的栈顶元素,执行 stack2.push(node),O(1)。
    • 否则,执行 stack2.push(stack2.peek()),O(1)。
  • 因此,整体的时间复杂度为 O(1)。

pop():

  • 同时从 stack1stack2 弹出栈顶元素,都是O(1)的操作。
  • 因此,整体的时间复杂度为 O(1)。

top():

  • 直接返回 stack1 的栈顶元素,O(1)的操作。
  • 因此,整体的时间复杂度为 O(1)。

min():

  • 直接返回 stack2 的栈顶元素,O(1)的操作。
  • 因此,整体的时间复杂度为 O(1)。

空间复杂度分析

  • 主栈 stack1 和辅助栈 stack2 都需要额外的空间来存储元素,因此空间复杂度是 O(n),其中 n 是推入栈的元素数量。

进阶版

进阶版 -> 使用差值:在一些情况下,我们可以使用差值来存储元素与当前状态下的最小值的关系,从而降低空间复杂度。(我会在下一篇博客里面讲的)

image-20231129200715576


http://www.kler.cn/a/157414.html

相关文章:

  • 【NLP 16、实践 ③ 找出特定字符在字符串中的位置】
  • java 基于冷热数据分离的思想设计LRU链表
  • 基于Python3编写的Golang程序多平台交叉编译自动化脚本
  • 浅谈目前我开发的前端项目用到的设计模式
  • 怿星科技联合赛力斯举办workshop活动,进一步推动双方合作
  • TypeScript进阶实战:构建可维护的企业级应用
  • 熬夜会秃头——beta冲刺Day7
  • 【开源】基于Vue.js的河南软件客服系统
  • 【Node-RED】http response收发实现
  • Shell数组函数:数组(一)
  • 如何制作教育培训小程序
  • 数字孪生是什么,是干什么用的?
  • 01 高等数学.武忠祥.0基础
  • 考虑光伏发电的配电网重构策略研究
  • 一次elasticsearch 查询瞬间超时案例分析
  • GEE:使用Roberts算子卷积核进行图像卷积操作
  • 【C语言】深入理解C语言中的数学运算和类型转换
  • Unity中C#使用协程控制Shader材质变化
  • unity3d模型中缺失animation
  • docker (镜像分层、阿里云镜像推送/拉去)-day02
  • Kontakt v7.7.2(音频采样器)
  • Golang实践录:读取xml配置文件
  • 基于mps的pytorch 多实例并行推理
  • C语言--每日选择题--Day35
  • Apache Sqoop使用
  • 详细学习Pyqt5的9种显示控件