打败算法 —— 最小栈
本文参考
出自LeetCode上的题库 —— 最小栈,根据官方的解法需要额外建立一个辅助栈,本文提出不需要额外空间的解法
https://leetcode-cn.com/problems/min-stack/
最小栈问题
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈
示例1:
输入:["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.
解题思路
一般的栈结构在进行pop操作时,要求将弹出的元素返回,但是本题保证了pop、top 和 getMin 操作总是在非空栈上调用,我们甚至都不需要在top和getMin函数中判断当前的栈是否为空,所以可以很简洁地设计这些基本的栈操作
向栈中压入元素时,官方题解额外用一个辅助栈存储此时的最小值,尽管这种解法非常容易理解,但是除栈本身外,带来了$O(n)$的空间复杂度。因此,为了将空间复杂度降到$O(1)$常数级别,我们仅在栈中存储与当前最小值相比的差值。理解的难度主要在于如何对差值进行处理,例如,在调用top时,如何将差值还原回本身的数值;在调用pop时,如何利用差值更新当前的最小值
差值解法
class MinStack:
def __init__(self):
# 模拟栈
self.stack = list()
# 记录最小值
self.min_value = -1
# 栈元素个数计数
self.cnt = 0
# 压栈
def push(self, val: int) -> None:
# 第一个元素
if not self.cnt:
self.stack.append(val)
self.min_value = val
else:
# 栈内存储差值
sub = val - self.min_value
self.stack.append(sub)
self.min_value = self.min_value if sub > 0 else val
self.cnt += 1
# 出栈
def pop(self) -> None:
self.cnt -= 1
sub = self.stack.pop()
# 差值 < 0 更新最小值
if sub < 0:
self.min_value -= sub
# 栈顶
def top(self) -> int:
sub = self.stack[-1]
if self.cnt > 1:
# 根据差值还原回原来的数值
return self.min_value if sub < 0 else sub + self.min_value
else:
# 栈中只有 1 个元素时直接返回
return sub
# 最小值
def get_min(self) -> int:
return self.min_value