LeetCode刷题记录----155.最小栈(Medium)

2025/9/8

155. 最小栈

题目(Medium):


我的思路:

这题需要实现的类需要满足两个条件:

①能够像栈一样进行操作

②能够常数时间复杂度找到最小值

其中条件①就是操作要先进后出,后进先出的意思。而条件二则暗示我们需要一直有某个东西指向最小值,以在需要的时候立马从这个指针中提取。

先想用常规栈和一个常数记录最小值,如果当前压入栈的元素比记录的最小值还小,那就更新最小值,这样看起来好像返回的时候就可以直接返回最小值了。

但是麻烦的地方在于弹出元素的时候,加入入栈的元素是{1,2,3,4,-1},我们当前知道最小值是-1,但是-1弹出了之后,我们肉眼可以看出来剩下的最小值是1。但是如果是栈的话,我们不可能直接去摸到这个栈底的元素,而需要一个能够直接-1弹出后指向1的指针。因此我们可以考虑用链表来实现。

如果用链表的话:

  • 我们可以维护一个topNode指针,每次入栈元素加入后就更新指向它,每次弹出元素就回退到上一个节点位置,同样可以满足栈的操作规则。同时可以看出要用双向链表
  • 同时我们可以有一个minNode指针指向当前最小值所在的节点,而这个节点会指向第二小的节点。(入栈的时候如果新加入的节点的值比原来minNode指向的节点的值还小的话,那它才会有指向上一个最小值的指针,注意第一个加入的值它一定是没有指向上一个最小值的指针的)

具体代码如下:

struct MyListNode {
    int val;        //当前的值
    MyListNode *next;     //下一个节点
    MyListNode * pre;     //上一个节点
    MyListNode *lastMin;      //上一个最小值节点位置

    MyListNode(int x) : val(x), next(nullptr), pre(nullptr), lastMin(nullptr) {}
};

class MinStack {
private:
    MyListNode* stack;    //链表模拟栈
    MyListNode* topNode;  //顶部指针
    MyListNode* minNode;  //当前最小值节点指针

public:
    MinStack() {
        cout << "最小栈初始化" <<endl;
        topNode = stack = new MyListNode(INT_MAX);
        minNode = nullptr;
    }
    
    void push(int val) {
        //常规栈:先进后出,后进的在顶上
        MyListNode* newNode = new MyListNode(val);
        //入栈
        topNode->next = newNode;
        newNode->pre = topNode;
        topNode = topNode -> next;
        
        if(minNode == nullptr){
            //说明此时栈中没有元素(那当然也没有最小元素了)
            minNode = newNode;
        }
        else{
            //否则和存在的最小节点进行比较
            if(val <= minNode->val){
                //更新最小节点位置
                newNode->lastMin = minNode;
                minNode = newNode;
            }
        }
    }
    
    void pop() {      
        //判断当前是否要更新最小值节点
        if(topNode->lastMin != nullptr)
            minNode = topNode->lastMin;

        //然后把该节点从链表中断开
        //如果当前最小值节点 就是要弹出的头节点 说明此时已经是栈中最后一个元素了
        if(minNode == topNode)
            minNode = nullptr;
        topNode = topNode->pre;
        delete topNode->next;
        topNode->next = nullptr;     
    }
    
    int top() {
        return topNode->val;
    }
    
    int getMin() {
        return minNode->val;
    }
};

时间复杂度:push,top,getMin,pop四个操作的时间复杂度都是O(1)

空间复杂度:O(N):取决于有多少个元素要入栈


更好的思路:

其实也可可以真的用栈的,只是要用两个栈:

  • 一个栈负责正常地入栈出栈
  • 另一个栈同步进行入栈出栈,只是入栈的时候的值一直只加入当前最小的值。同时要获取最小值也是在这个栈中获取

具体代码如下:

class MinStack {
private:
    stack<int> mainStack;
    stack<int> minStack;

public:
    MinStack() {
        cout << "最小栈初始化" <<endl;
        minStack.push(INT_MAX);
    }
    
    void push(int val) {
        mainStack.push(val);
        minStack.push(min(minStack.top(), val));    //最小值栈一直入的是当前最小的值
    }
    
    void pop() {      
        mainStack.pop();
        minStack.pop();  
    }
    
    int top() {
        return mainStack.top();
    }
    
    int getMin() {
        return minStack.top();
    }
};

时间复杂度:push,top,getMin,pop四个操作的时间复杂度都是O(1)

空间复杂度:O(N):取决于有多少个元素要入栈


总结:

①对于要快速指定和查找元素的需求,考虑有灵活指针的链表是可以的

②对于这里的栈的需求,用的是一个主栈和一个辅助栈,主栈负责正常操作,辅助栈在正常操作的前提下,对入栈的时候的元素进行了一些大小判断。然后这两个栈同步操作,才能达到我们需要的功能

### LeetCode 题基础语法入门教程 LeetCode 是程序员提升算法能力的重要平台之一,掌握其基础语法对于高效解题至关重要。以下是关于 C++ 和 Java 的基础语法要点以及如何应用这些知识来解决 LeetCode 上的问题。 #### 1. 数据类型与变量 C++ 提供了多种基本数据类型,包括但不限于 `int`、`long` 和 `double` 等[^2]。在编写程序时,应根据具体需求选择合适的数据类型以优化内存使用和计算效率。例如,在处理大规模数值运算时,推荐优先考虑浮点数或长整型以避免溢出问题。 #### 2. 控制流语句 控制流是编程的核心部分,它决定了代码执行路径的选择逻辑。常用的条件分支结构如 `if...else` 或者更复杂的多路判断工具——`switch case` 可帮助开发者根据不同输入情况采取相应操作。此外还有循环机制(for/while),它们允许重复执行某段特定指令直到满足终止条件为止。 #### 3. 容器类简介及其应用场景分析 为了更好地管理和存储大量动态变化的信息单元组群对象集合体概念模型抽象表示形式即我们常说的各种标准模板库(STL)组件实例化后的实体形态表现出来的东西叫做容器(Container),其中最常用的一些包括: - **Vector**: 动态数组,支持随机访问并能在尾部快速增删元素。 - **Set & Unordered_Set**: 分别代表有序集合并具备查找功能的哈希表版本;前者按升序排列后者则不关心顺序只关注唯一性检验速度更快些时候会用到find()方法来进行成员存在性的检测工作流程简化很多哦~ - **Map & Multimap**: 键值映射关系管理利器,能够轻松实现一对一或多对一关联查询任务目标达成效果显著提高工作效率的同时也减少了错误发生的可能性几率大大降低啦!另外还有一种叫Unorderd_Map变种形式同样适用于某些特殊场合条件下呢😊 #### 4. 特殊用途的数据结构介绍 - Deque (双端队列) Deque是一种可以在两端都进行插入删除操作非常灵活方便的一种线性序列结构形式表达方式呈现出来的样子感觉特别棒👍🏻通过下面这个例子我们可以看到它是怎么被创建出来的:`Deque<Integer> deque = new LinkedList<>();` 这样我们就得到了一个基于链接列表实现原理构建而成的新对象实例可供后续进一步开发拓展之用了呀😄[^3] #### 5. 关于(Stacks)的知识补充说明 最后值得一提的是有关Stack方面的内容知识点分享给大家知道一下吧~原来啊,在Standard Template Library里面啊,我们的老朋友Stack其实背后隐藏着秘密武器呢🧐那就是它可以由三种不同的底层支撑技术方案任选其中之一作为实际运行环境下的物理载体介质哟😎分别是向量(Vector),双向队列(Deque)或者是简单的单链表(List)...怎么样是不是很神奇呢😉[^4] ```java // 示例:Java 中 Stack 的简单使用 import java.util.Stack; public class Main { public static void main(String[] args) { Stack<Integer> stack = new Stack<>(); // 添加元素 stack.push(10); stack.push(20); System.out.println("Top element is: " + stack.peek()); // 输出顶部元素 // 删除顶部元素 stack.pop(); System.out.println("After popping, top element is: " + stack.peek()); } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萘柰奈

谢谢老板喵

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值