Leetcode 155 Min Stack 最小栈 解法分析及相关题目衍生获取队列最大值

文章目录

    • @[toc]
    • 题目要求
    • 解法分析
      • 辅助栈法
      • 链表定义法
    • 相关题目——O(1)时间获取队列最大值

自己实现的LeetCode相关题解代码库:https://github.com/Yuri0314/Leetcode


题目要求

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

  • 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.

提示:

  • poptopgetMin操作总是在 非空栈 上调用。

解法分析

题目所要求实现的push、pop和top操作,普通的栈都提供,并且均可以在O(1)的时间复杂度内做到,因此本题的难点在于如何在常数时间内实现getMin操作,即获取栈内元素最小值。

仔细分析栈的特点不难发现一共需要考虑4种情境:

  1. 入栈元素值比现有最小元素小

  2. 入栈元素值比现有最小元素大

  3. 出栈元素是当前最小元素

  4. 出栈元素不是当前最小元素

可以看到,上面的2、4情境对当前栈内最小值无影响,所以只需考虑入栈和出栈最小值的情境即可。可以使用两种解法,下面依次进行说明。

辅助栈法

除了保存数据的栈之外,可以使用一个额外的辅助栈来存储一些辅助数据。

该辅助栈的栈顶要求保存的始终是当前栈元素中的最小元素。为了实现这一要求,针对上面的4种情境,辅助栈的对应做法是:

  1. 辅助栈不处理(因为最小值已经在数据栈中,该新值不影响)
  2. 将该值压入辅助栈(因为该值会影响当前栈中的最小元素值)
  3. 弹出辅助栈栈顶元素(因为辅助栈栈顶始终是栈中最小元素)
  4. 辅助栈不处理(因为最小值还在数据栈中,出栈元素不影响)

总结就是,数据栈当作普通栈来使用,而辅助栈只有当入栈元素比其栈顶元素小时再入栈,当数据栈出栈元素等于辅助栈栈顶元素时(即出栈当前最小元素)弹出辅助栈栈顶元素。

class MinStack {
     
    private Stack<Integer> stack;
    private Stack<Integer> minStack;

    /** initialize your data structure here. */
    public MinStack() {
     
        stack = new Stack<Integer>();
        minStack = new Stack<Integer>();
    }
    
    public void push(int x) {
     
        stack.push(x);
        if (minStack.empty())
            minStack.push(x);
        else if (x <= minStack.peek())
            minStack.push(x);
    }
    
    public void pop() {
     
        int item = stack.pop();
        if (item == minStack.peek())
            minStack.pop();
    }
    
    public int top() {
     
        return stack.peek();
    }
    
    public int getMin() {
     
        return minStack.peek();
    }
}

链表定义法

除了使用一个辅助栈来帮助实现,还可以重新定义一个栈结构,其栈中每一个元素是自定义的一个节点,节点中存储了当前元素的值和当前元素作为栈顶元素时的最小值。

每当插入一个新元素时,比较该元素值和目前栈顶结点中最小值,将其更小的那个值作为该插入新结点的最小值,然后将生成的新节点入栈。这样无论入栈还是出栈,当前栈顶保存结点都有存储对应的当前最小值,直接取出即可。

class MinStack {
     
    private class Node {
     
        int val;
        int min;
        Node next;

        Node(int val, int min) {
     
            this(val, min, null);
        }

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

    private Node head;

    /** initialize your data structure here. */
    public MinStack() {
     
        head = null;
    }
    
    public void push(int x) {
     
        if (head == null)
            head = new Node(x, x);
        else
            head = new Node(x, Math.min(x, head.min), head);
    }
    
    public void pop() {
     
        head = head.next;
    }
    
    public int top() {
     
        return head.val;
    }
    
    public int getMin() {
     
        return head.min;
    }
}

相关题目——O(1)时间获取队列最大值

与最小栈类似,要求设计一个队列,其需要支持在O(1)的时间满足:出队、入队和求最大元素的操作。

从最小栈的问题类推过来,同样有4种情境:

  1. 入队元素比最小元素小
  2. 入队元素比最小元素大
  3. 出队元素是当前最小元素
  4. 出队元素不是当前最小元素

与栈的结构不同,队列是双端操作,所以上述对最小栈的实现中的第二种方法无法适用改写到该问题中。

本题可借用最小栈的辅助栈解法,使用一个数据队列作为普通队列使用,一个辅助队列来实现O(1)时间获取最大元素,其中的元素值保持单调性。

  • 在执行入队操作时,数据队列正常操作,而对于辅助队列,只有当入队元素比辅助队列队尾元素小的时候才入队,否则将辅助队列队尾元素删除直到队尾元素大于等于要入队元素再入队;
  • 在执行出队操作时,数据队列正常操作,而对于辅助队列,如果出队元素等于辅助队列的队头元素时,辅助队列也进行出队。

在这种实现下,要获取队列最大值只需要获取辅助队列队头元素值即可。

public class MyQueue {
     
    private Deque<Integer> queue = new LinkedList<Integer>();
    private Deque<Integer> monoQueue = new LinkedList<Integer>();

    public void push(int x) {
     
        queue.addLast(x);
        while (!monoQueue.isEmpty() && x > monoQueue.getLast()) 
            monoQueue.removeLast();
        monoQueue.addLast(x);
    }

    public int pop() {
     
        int x = queue.removeFirst();
        if (x == monoQueue.getFirst())
            monoQueue.removeFirst();
        return x;
    }

    public int getMax() {
     
        return monoQueue.getFirst();
    }
}

你可能感兴趣的:(算法,leetcode,Java,java,算法,leetcode,栈,队列)