算法 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

执行耗时: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 变形与延伸

=== 待续 ===

你可能感兴趣的:(算法 1.3.1 最小栈 【leetcode 155】)