30.包含min函数的栈

30 包含min函数的栈

1 题目描述

​ 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.min();   --> 返回 -2.

2 题目分析

​ 解法一:用辅助栈的思想,即创建一个辅助栈用来存储当前情况下的最小值,初始辅助栈里存储Integer.MAX_VALUE,之后每次主栈入栈时都判断当前元素元辅助栈栈顶元素的大小,将较小者入栈,出栈时两个栈一同出栈,这样求最小元素时只需返回辅助栈的栈顶元素即可,这也是最常用的想法,一般面试官到此就会满足,但不排除要求更高的面试官会问不使用辅助栈如何解决?看解法二!

​ 解法二:用差值法的思想,我们先每次入栈时将栈顶元素和min值做差后的结果加入栈中,有个很巧妙的结论:

  • 入栈时:当差值大于等于0时,min值不变;否则将min值变为当前元素。

  • 出栈时:当要出栈元素大于等于0时,min值还是不变;否则说明当前元素出栈后min值会变,具体变成什么?答案是当前的min-栈顶元素(因为栈顶元素是负数,因此会变大)画个图就容易理解了:

在这里插入图片描述

​ 解法三:上述两种解法已经足够应对面试了,这里的第三种解法了解即可,其实也是解法一的变种,在入栈的时候当min值变化时先将原来的min加入的栈再入栈,这样出栈时如果栈顶元素等于min值时就连续出两次栈,将第二次的值更新为min。只不过这样要严格判断只要入栈时的min小于或者等于min时就入两次栈!

3 代码

  • 解法一:
/*
        包含min函数的栈,用O(1)的时间复杂度实现,快速想到用辅助栈去存储最小元素,初始时辅助栈中加入最大值,即每次主栈入栈时判断当前元素与辅助栈中栈顶元素相比,然后就小的加入辅助栈中。
        这样求最小元素只需返回辅助栈的栈顶元素即可,出栈操作时辅助栈和主栈同时出栈即可。
     */
static class MinStack {

    Deque<Integer> stack;
    Deque<Integer> min;

    /** initialize your data structure here. */
    public MinStack() {
        stack = new LinkedList<>();
        min = new LinkedList<>();
        min.push(Integer.MAX_VALUE);
    }

    public void push(int x) {
        stack.push(x);
        min.push(Math.min(min.peek(), x));
    }

    public void pop() {
        stack.pop();
        min.pop();
    }

    public int top() {
        return stack.peek();
    }

    public int min() {
        return min.peek();
    }
}
  • 解法二:
/*
        栈中永远存储当前值和min值的差值,这样的目的是判断当前值和min值哪一个是当前的真正最小值,也能根据公式去取最小值:
            1. 如果差值是大于等于0的,说明当前真正的最小值是后者,入栈时将差值加入,出栈后min值不用更新
            2. 如果差值是小于0的,说明当前的最小值是前者,那么入栈时加入到就是负数,更新min值为当前值,出栈由于出的就是最小值因此需要还原上一个min值,这时好处就是我们可以用的当前min减去栈顶元素来回溯的之前的min
        注意:差值法用Java容易造成越界,因此需要改成long类型
     */
static class MinStack {

    Deque<Long> stack;
    long min;

    /** initialize your data structure here. */
    public MinStack2() {
        stack = new LinkedList<>();
        min = Integer.MAX_VALUE;
    }

    public void push(int x) {
        /*if (x < min) {
                // 如果x小于min,将差值入栈,更新min
                stack.push(x - min);
                min = x;
            } else {
                // 直接将差值入栈
                stack.push(x - min);
            }*/
        stack.push(x - min);
        if (x < min) min = x;
    }

    public void pop() {
        if (stack.isEmpty()) return;
        // 出栈时,判断当前栈顶元素是不是负的
        long val = stack.pop();
        if (val < 0) {
            // 回溯min
            min = min - val;
        }
    }

    public int top() {
        long top = stack.peek();
        if (top > 0) return (int) (top + min);
        else return (int) min;  // 当前min就是出栈的元素
    }

    public int min() {
        return (int) min;
    }
}
  • 解法三:
/*
        思路:其实很多同学第一个想到的并不是辅助栈,而是用一个变量去存储最小值,这样求最小值的时候直接返回该变量就行了,但是有一个问题就是如果出栈的元素就是
        最小值的时候我们不知道第二小的元素是谁,以此类推,第二小的出栈我们也不知道第三小的元素是谁?那么有什么方法去保存这些最小,第二小这些元素呢,可以在放入
        最小值元素前就把上次的最小值先放入栈中保存,这样最小值出栈后,再出栈的元素就是次小的元素了。多出一次栈就能解决问题,代码就简单了。
     */

static class MinStack {

    Deque<Integer> stack;
    int min;

    /** initialize your data structure here. */
    public MinStack1() {
        stack = new LinkedList<>();
        min = Integer.MAX_VALUE;
    }

    public void push(int x) {
        // 入栈之前先判断当前元素与min的大小,如果小或等的话则先把上次的min放进去,然后更新min值
        if (x <= min) {
            stack.push(min);
            min = x;
        }
        stack.push(x);
    }

    public void pop() {
        // 出栈时也先判断栈顶元素和min的关系,如果相等则连续出两次栈
        if (!stack.isEmpty() && stack.pop() == min) {
            min = stack.pop();
        }
    }

    public int top() {
        return stack.peek();
    }

    public int min() {
        return min;
    }
}

参考LeetCode大神:windliangL6题解

4 智商被碾压的解法

​ 定义一个链表结构来实现栈的基本功能。

class MinStack {
    class Node{
        int value;
        int min;
        Node next;

        Node(int x, int min){
            this.value=x;
            this.min=min;
            next = null;
        }
    }
    Node head;
    //每次加入的节点放到头部
    public void push(int x) {
        if(null==head){
            head = new Node(x,x);
        }else{
            //当前值和之前头结点的最小值较小的做为当前的 min
            Node n = new Node(x, Math.min(x,head.min));
            n.next=head;
            head=n;
        }
    }

    public void pop() {
        if(head!=null)
            head =head.next;
    }

    public int top() {
        if(head!=null)
            return head.value;
        return -1;
    }

    public int getMin() {
        if(null!=head)
            return head.min;
        return -1;
    }
}
public int top() {
    if(head!=null)
        return head.value;
    return -1;
}

public int getMin() {
    if(null!=head)
        return head.min;
    return -1;
}

}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值