算法 1.3.1 最小栈 【leetcode 155】

题目描述

最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。

示例:
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

数据结构

  • 数组(栈)、链表(链栈)

算法思维

  • 遍历、状态机、快照、栈特性(LIFO)

解题要点

  • 使用快照记录状态信息
  • 利用栈的 LIFO 特性,实现状态信息的 更新/回滚

解题思路

一. Comprehend 理解题意
  • 需要自实现一个栈结构
  • 除了基础的入栈、出栈、查看栈顶功能外,还需要实现一个查询最小值的功能
二. Choose 选择数据结构与算法
解法一:使用数组实现栈结构,栈的最小值通过遍历数组获得
  • 数据结构:数组
  • 算法思维:遍历
三. Code 编码实现基本解法
class MinStack {

        //1.声明存放数据的数组和栈顶指针
        Integer[] arr;
        int index;

        /**
         * initialize your data structure here.
         */
        public MinStack() {
            //2.构造器中进行参数初始化
            arr = new Integer[1000];
            index = -1;
        }

        public void push(int x) {
            //3.入栈 -- 自动装箱
            arr[++index] = x;

            //数组扩容
            if (index == arr.length*0.8){
                Integer[] arr1 = new Integer[arr.length*2];
                for (int i=0; i<arr.length; i++){
                    arr1[i] = arr[i];
                }
                arr = arr1;
            }
        }

        public void pop() {
            if(index < 0) return;
            //4.出栈 -- 相当于删除栈顶元素 -- 栈顶赋值为 null
            arr[index--] = null;
        }

        public int top() {
            if (index < 0) return Integer.parseInt(null);
            //5.返回栈顶元素 -- 自动拆箱
            return arr[index];
        }

        public int getMin() {
            if (index < 0) return Integer.parseInt(null);

            //6.遍历数组寻找最小值
            int minus = arr[0]; //最小值
            for (int i=1; i<=index; i++){
                minus = arr[i] < minus ? arr[i] : minus;
            }
            return minus;
        }
    }

执行耗时:65 ms,击败了 7.35% 的Java用户
内存消耗:40.2 MB,击败了 55.30% 的Java用户
时间复杂度:O(n) -- 数组的遍历 O(n),数组的扩容 O(n)
空间复杂度:O(n) -- 数组的内存空间 O(n)

四. Consider 思考更优解

改变算法思维!

思路:
  • 每次获取最小值时,都需要重新 遍历 数组,效率很低
  • 不难发现,每个元素在被压入栈顶的时候,栈的最小值都只有 两种状态
    1.当前元素为最小值(状态一:某个新的数值)
    2.以前的最小值仍为最小值(状态二:原数值)
  • 能否利用栈的特性,弹出栈顶元素的时候,将最小值的状态变化也一并弹出?
方法:
  • 让每个元素都携带一个“快照”,记录此元素入栈后(这一时刻)栈的最小值。
  • 当前栈的最小值 == 当前栈顶元素快照中的值
  • 当栈顶元素被弹出,上个元素成为栈顶,上个快照被启用,最小值回滚
解法二:使用链表实现栈结构,栈的最小值由栈顶元素携带
  • 数据结构:链表(链栈)
  • 算法思维:状态机、快照、栈特性(LIFO)
五. Code 编码实现最优解
 class MinStack {

        //1.声明一个内部类 -- 链表节点
        private class Node{
            int val; //当前节点值
            int min; //最小值快照 -- 当前节点入栈后,栈的最小值

            Node next; //指针

            public Node(){}

            public Node(int val, int min, Node next){
                this.val = val;
                this.min = min;
                this.next = next;
            }
        }

        //2.声明一个链表头 -- 栈顶
        Node head;

        /**
         * initialize your data structure here.
         */
        public MinStack() {
            //3.初始化链栈
            head = null;
        }

        public void push(int x) {
            //4.节点入栈时,判断链表长度
            if (head == null) {
                head = new Node(x,x,null);
            } else {
                //5.每次入栈时,比较最小值,将此刻的最小值存入 new Node().min 中
                head = new Node(x,Math.min(x,head.min),head);
            }
        }

        public void pop() {
            //6.出栈时,直接弹出即可,最小值随之回滚
            if (head != null) head = head.next;
        }

        public int top() {
            //7.查栈顶
            return head != null ? head.val : Integer.parseInt(null);
        }

        public int getMin() {
            //8.查最小值 -- 其实就是查当前栈顶的最小值快照
            return head != null ? head.min : Integer.parseInt(null);
        }
    }

执行耗时:6 ms,击败了 96.02% 的Java用户
内存消耗:40.3 MB,击败了 36.21% 的Java用户
时间复杂度:O(1) -- 栈顶元素的直接查询 O(1)
空间复杂度:O(n) -- 链栈的内存空间 O(n)

六. Change 变形与延伸

=== 待续 ===

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容