【栈与队列】力扣155:最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int 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.
要做出这道题目,首先要理解栈结构先进后出的性质。
对于栈来说,如果一个元素 a 在入栈时,栈里有其它的元素 b, c, d,那么无论这个栈在之后经历了什么操作,只要 a 在栈中,b, c, d 就一定在栈中,因为在 a 被弹出之前,b, c, d 不会被弹出。
因此,在操作过程中的任意一个时刻,只要栈顶的元素是 a,那么就可以确定栈里面现在的元素一定是 a, b, c, d。
- 辅助栈和数据栈同步
可以在每个元素 val 入栈时把当前栈的最小值 min 存储起来。在这之后无论何时,如果栈顶元素是 val,就可以直接返回存储的最小值 min。
- 算法:
只需要设计一个数据结构,使得每个元素 val 与其相应的最小值 min 时刻保持一一对应。因此我们可以使用一个辅助栈,与元素栈同步插入与删除,用于存储与每个元素对应的最小值。- 当一个元素要入栈时,取当前辅助栈的栈顶存储的最小值,与当前元素比较得出最小值,将这个最小值插入辅助栈中
- 当一个元素要出栈时,把辅助栈的栈顶元素也一并弹出
- 在任意一个时刻,栈内元素的最小值就存储在辅助栈的栈顶元素中
import math # 调用math模块
class MinStack:
def __init__(self):
self.stack = []
self.minStack = [math.inf] # 定义辅助栈并初始化
'''
注意:如果没有初始化辅助栈,在向其push新元素时会显示list index out of range,因为初始时辅助栈内没有minStack[-1],无法比较
'''
def push(self, val: int) -> None:
self.stack.append(val)
self.minStack.append(min(val, self.minStack[-1])) # 辅助栈的栈顶是压入新元素val前栈stack的最小值,因此新栈顶压入的是旧的最小值和新元素中的最小值。
'''
若定义时不初始化辅助栈,可以这样写
def push(self, val: int) -> None:
if not self.minStack:
self.minStack.append(val)
else:
self.minStack.append(min(val, self.minStack[-1]))
'''
def pop(self) -> None:
# pop、top 和 getMin 操作总是在非空栈上调用
if self.stack:
self.stack.pop()
# if self.minStack:
self.minStack.pop()
def top(self) -> int:
# if self.stack:
return self.stack[-1]
def getMin(self) -> int:
# if self.minStack:
return self.minStack[-1]
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(val)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
时间复杂度:对于题目中的所有操作,时间复杂度均为 O(1)。因为栈的插入、删除与读取操作都是 O(1),所定义的每个操作最多调用栈操作两次。
空间复杂度:O(n),其中 n 为总操作数。最坏情况下会连续插入 n 个元素,此时两个栈占用的空间为 O(n)。
- 辅助栈和数据栈不同步
由【辅助栈和数据栈同步】的思想,当数据栈进来的数越来越大的时候,要在辅助栈顶放置和当前辅助栈顶一样的元素,这样做有点“浪费”。基于这一点做一些“优化”,但是在编码上就要注意一些边界条件:- 辅助栈为空的时候,必须放入新元素
- 新元素 <= 辅助栈栈顶元素的时候,才放入。特别注意这里“等于”要考虑进去,因为出栈的时候,连续的、相等的并且是最小值的元素要同步出栈
- 出栈的时候,辅助栈的栈顶元素等于数据栈的栈顶元素,才出栈
总结:出栈时,最小值出栈才同步;入栈时,最小值入栈才同步。
对比:【同步栈】所有操作都同步进行,思路清楚,所以调试代码、定位问题也简单,不考虑空间的话其实更好一些。【不同步栈】虽然减少了一些空间,但是在“出栈”、“入栈”的时候还要做判断,也有性能上的消耗。
class MinStack:
def __init__(self):
self.stack = []
self.minStack = [] # 辅助栈
def push(self, val: int) -> None:
self.stack.append(val)
# 关键 1:辅助栈为空的时候,必须放入新元素
# 关键 2:新元素 <= 辅助栈栈顶元素的时候才放入,特别注意`=`要考虑进去
if len(self.minStack) == 0 or val <= self.minStack[-1]:
self.minStack.append(val)
def pop(self) -> None:
# 关键3:出栈的时候,数据栈的元素一定出栈,而辅助栈的栈顶元素=数据栈的栈顶元素才出栈
top = self.stack.pop()
if self.minStack and top == self.minStack[-1]:
self.minStack.pop()
return top # 为什么这一句不能放在line 17呢???
def top(self) -> int:
if self.stack:
return self.stack[-1]
def getMin(self) -> int:
if self.minStack:
return self.minStack[-1]
时间复杂度:O(1),“出栈”、“入栈”、“查看栈顶元素”的操作不论数据规模多大,都只有有限个步骤。
空间复杂度:O(N),这里 N 是读出的数据的个数。
作者:liweiwei1419
链接:https://leetcode.cn/problems/min-stack/solution/shi-yong-fu-zhu-zhan-tong-bu-he-bu-tong-bu-python-/
- 不借助辅助栈,直接用数据栈储存元组数据
题目要求在常数时间内获得栈中的最小值,因此不能在 getMin() 的时候再去计算最小值,最好应该在 push 或者 pop 的时候就已经计算好了当前栈中的最小值。
思路:
- 可以用一个栈,这个栈同时保存的是【每个元素 val 进栈的时候的值】 与【插入该值后的栈内最小值】。即每次新元素 val 入栈的时候保存一个元组:(新元素 val, 栈内最小值)。
- 这个元组是一个整体,同时进栈和出栈。即栈顶同时有值和栈内最小值,
top()函数是获取栈顶的当前值,即栈顶元组的第一个值;getMin()函数是获取栈内最小值,即栈顶元组的第二个值;pop()函数是删除栈顶的元组。 - 每次新元素入栈时,需要求新的栈内最小值:
- 当栈为空,保存元组 (val, val)
- 当栈不空,保存元组 (val, min(此前栈内最小值, val))),即比较当前新插入元素 val 和 当前栈内最小值(即栈顶元组的第二个值)的大小。
- 出栈:删除栈顶的元组。
class MinStack:
def __init__(self):
self.stack = []
def push(self, val: int) -> None:
if not self.stack:
self.stack.append((val, val)) # 注意括号
else:
self.stack.append((val, min(val, self.stack[-1][1]))) # -1 定位到栈顶,在元组中,0 是新元素,1 是当前最小值
def pop(self) -> None:
self.stack.pop() # 整个元组都弹出
def top(self) -> int:
return self.stack[-1][0] # 返回栈顶元素
def getMin(self) -> int:
return self.stack[-1][1] # 返回元组的第二个元素

可以看出复杂度是差不多的

浙公网安备 33010602011771号