最小栈
题目
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x) -- 将元素 x 推入栈中。
- pop() -- 删除栈顶的元素。
- top() -- 获取栈顶元素。
- getMin() -- 检索栈中的最小元素。
示例:
MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin(); --> 返回 -2.
元素和最小值结对法
将「入栈元素」和「其入栈后栈内最小值」组成一对,入栈、出栈操作都是操作对。
举个例子,创建一个栈,初始为空,不存在最小值。
当压入值-2
时,最小值变为-2
。所以同时压入值-2
和最小值-2
。
当压入值0
,最小值依旧为-2
。所以同时压入值0
和最小值-2
。
当压入值-3
时,最小值变为-3
。所以同时压入值-3
和最小值-3
。
将值和最小值结对压入栈,保证栈顶元素就是最小值。
当弹出元素时,将值和最小值一同弹出。之前最小值从-2
变为-3
是因为值-3
的加入,现在-3
被移出栈了,最小值也就回复到之前的-2
。
代码实现
使用JDK提供的ArrayList
来实现有序列表,栈就只能在一端增加删除元素的有序列表。
入栈的时候将值和最小值成对入栈。最小值通过取之前最小值和当前压入值之中最小的值得到。
出栈的时候也成对出栈。
值和最小值是成对压入、弹出的,栈顶元素是最值,从顶往下第二个元素就是罗辑上的「栈顶」元素。
值和最小值是成对压入、弹出的,栈顶元素就是最小值。
复杂度分析
时间复杂度
push
只在栈顶操作,且每次都是固定压入两个元素(值和最小值),跟栈中的数据量无关。所以时间复杂度为。
pop
也只在栈顶操作,且每次都是固定弹出两个元素(值和最小值),跟栈中数据量无关。所以时间复杂度为。
top
固定读取从顶往下第二个元素,跟栈中数据量无关。所以时间复杂度为。
getMin
固定读取栈顶元素,跟栈中数据量无关。所以时间复杂度为。
空间复杂度
本结构将每个值都扩展为两个值组成的对,假设值的数据量为,则栈中将有个元素。空间复杂度为,消掉常量为。
双栈法
创建两个栈,一个用于存储值,另一个用于存储最小值。
举个例子,创建两个栈dataStack
和minValueStack
。
把值-2
压入dataStack时,最小值变为-2
,将最小值-2
同时压入minValueStack。
把值0
压入dataStack时,最小值没有发生变化,所以就不压入minValueStack。
把值-3
压入dataStack时,最小值变为-3
,所以把-3
同时压入minValueStack。
minValueStack栈顶元素就是最小值。
从dataStack弹出值时,要检查minValueStack栈顶元素是否等于dataStack栈顶元素,若是则表明「当前最小值」被移出了dataStack,minValueStack要相应地将其弹出。
代码实现
使用JDK提供的ArrayList
实现两个栈,dataStack存储值,minValueStack存储最小值。
压入值时,先将值压入dataStack,再比较新压入值跟当前最小值。若新压入值小于或等于当前最小值,则最小值被改变,将新值同时压入minValueStack。
弹出值时,除了将dataStack栈顶元素弹出,同时若minValueStack栈顶元素等于dataStack弹出元素,则意味着minValueStack栈顶的最小值被移除了,所以要同步弹出minValueStack栈顶元素。
dataStack专门存储值,所以dataStack栈顶元素即即最后压入元素。
minValueStack专门存储最小值,minValueStack栈顶元素即最小值。
复复杂分析
时间复杂度
push
操作最多在两个栈中分别压入一个元素,时间复杂度为。
pop
操作最多从两个栈中分别弹出栈顶元素,时间复杂度为。
top
操作直接从栈dataStack读取栈顶元素,时间复杂度为。
getMin
操作直接从栈minValueStack读取栈顶元素,时间复杂度为。
空间复杂度
本结构使用两个栈,假设值的数据量为,则dataStack佔用个存储空间,minValueStack最坏情况下(所有值都相等或值以降序顺序压入)佔用个存储空间。总体空间复杂度为。