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

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

来源:力扣(LeetCode)第155题

链接:https://leetcode-cn.com/problems/min-stack

题目解析:

这道题需要用到一个辅助栈来帮忙,较为暴力的方法是使用python的内置函数min直接算出最小值,或者遍历整个栈将最小值算出。但是题目要求getMin的时间复杂度是常数级的,也就是O(1)。因此上述的方法行不通,需要奇妙的运用到栈的特性来解题。

解题思路:

首先要有两个栈,一个是正常的数据栈(stack),另一个是只存最小值的辅助栈(minStack)。数据栈正常进,出数据,辅助栈在每次进栈和出栈时要判断。如果push的值比辅助栈的栈顶元素要小(或者相等),那么就把它压入辅助栈。同理,如果pop的值比辅助栈的栈顶元素要小(或者相等),那么就讲辅助栈的栈顶元素弹出。

为什么要这样做呢?

其实这很好理解。第一个入栈的元素进栈后,最小值肯定就是它,所以将它放入辅助栈,让它成为判断的目标。如果有比第一个入栈的元素大的元素进栈的话,那其实不用管它们,因为最小值不可能是它们,所以不用把它们放进辅助栈中。如果有栈的元素小于第一个入栈的元素的话,那么就只需要将小于的元素压入辅助栈中,使其成为栈顶元素。如此一来,我们就改变判断的目标,将后面push的元素与这个元素进行比较,直到辅助栈又有一个比它小的元素进栈。

出栈也是一个道理,只要出栈的元素比辅助栈的栈顶元素小或相等的话。就将辅助栈的栈顶元素弹出。

这么一来其实就很明朗了。辅助栈的栈顶元素永远都是最小的元素,而整个辅助栈的元素的值从上自下依次在逐渐变大,直到栈底元素,也就是数据栈中第一个入栈的元素。如果有元素比数据栈的栈底元素大的话,根本不用考虑,因为在出栈的过程中,它们永远都会比数据栈的栈底元素先出栈,所以它们直到数据栈中的数据弹光也不可能作为最小值。那么就不用放入栈中。

简单理解:

使用两个栈,一个用于正常存储数据(stack),另一个用于把可能作为最小值的数据从大到小依次压入栈(minStack),也就是把每次比minStack的栈顶元素小的数压入minStack。

然后pop操作的时候,只要关注一下当前栈的最小值有没有被弹出去,有的话minStack也要出栈。

getMin的值就是辅助栈的最小值

时间复杂度:

非常短,只要进行依次获取栈顶元素的操作即可。为O(1)

实现代码:(Python)

class MinStack:
    def __init__(self):
        self.stack = []
        self.minStack = []

    def push(self, x: int) -> None:
        if len(self.minStack) == 0 or x <= self.minStack[-1]:
            self.minStack.append(x)
        self.stack.append(x)

    def pop(self) -> None:
        if self.stack.pop() <= self.minStack[-1]:
            self.minStack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def getMin(self) -> int:
        return self.minStack[-1]

总结:

题目理解起来不算太难,就是要能想到使用栈这种数据结构。总体的思路就是使用辅助栈来实现。这种方式还可以用来进行别的需求,使用辅助栈来过滤掉一部分不要的元素,同时保证每次需要的元素都在辅助栈的栈顶,而在进行pop操作的时候,也可以顺畅的通过判断来检查所需要的元素是否以及被弹出栈外。