Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
- push(x) -- Push element x onto stack.
- pop() -- Removes the element on top of the stack.
- top() -- Get the top element.
- getMin() -- Retrieve the minimum element in the stack.
Example:
MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> Returns -3. minStack.pop(); minStack.top(); --> Returns 0. minStack.getMin(); --> Returns -2.
这是一道有意思的设计题。最小栈其余都和普通栈类似,但是最后一个要求:在常数时间内返回stack的最小值需要特别的处理。
拿到这道题我的第一思路是在栈的倒数第二位存储最小值,但是发现后续弹出和更新都很麻烦,遂放弃,但是这种解法其实可以再辅以最小值做一个改进实现。
目前比较主流的思路是,除了本身的stack以外,再维护一个保存最小值的stack:trackMin。当x<=trackMin[-1]时,执行trackMin.append(x)。以记录新的最小值。但是注意等于的情况下也要执行append,不然有重复最小元素相继入栈,又相继弹出时,弹出前面的元素时,trackMin的栈顶存的不再是最小元素:
此时如果pop,则变为
xi: 3 2 1 2
trackMin: 3 2
然而实际栈里的最小值仍旧为1,这个1因为重复数字的关系在trackMin中丢失。代码如下:
class MinStack(object): def __init__(self): """ initialize your data structure here. """ self.stack = [] self.trackMin = [] def push(self, x): """ :type x: int :rtype: void """ if not self.trackMin or self.trackMin[-1]>=x: self.trackMin.append(x) self.stack.append(x) return def pop(self): """ :rtype: void """ if len(self.stack) == 0: return if self.stack.pop()==self.trackMin[-1]: self.trackMin.pop() def top(self): """ :rtype: int """ if self.stack: return self.stack[-1] else: return None def getMin(self): """ :rtype: int """ if self.trackMin: return self.trackMin[-1] else: return None # Your MinStack object will be instantiated and called as such: # obj = MinStack() # obj.push(x) # obj.pop() # param_3 = obj.top() # param_4 = obj.getMin()
除了使用两个栈的思路,使用一个栈也是可以实现的,而且有多种思路:一个是使用一个栈,但是栈中存的是,值和压入该值时栈内的最小值这个键值对,或者tuple。显然这种空间复杂度是2(n)比上面双栈在平均情况下,内存消耗要大。这种解法的Java实现如下:
class MinStack { static class Element { final int value; final int min; Element(final int value, final int min) { this.value = value; this.min = min; } } final Stackstack = new Stack<>(); public void push(int x) { final int min = (stack.empty()) ? x : Math.min(stack.peek().min, x); stack.push(new Element(x, min)); } public void pop() { stack.pop(); } public int top() { return stack.peek().value; } public int getMin() { return stack.peek().min; } }
另外一种解法是维护一个最小值min,每个进栈的元素在栈中存储的实际是和min的gap值,但是这种解法存在overflow的可能,运算也比较大,Java代码实现如下:
public class MinStack { long min; Stackstack; public MinStack(){ stack=new Stack<>(); } public void push(int x) { if (stack.isEmpty()){ stack.push(0L); min=x; }else{ stack.push(x-min);//Could be negative if min value needs to change if (x x; } } public void pop() { if (stack.isEmpty()) return; long pop=stack.pop(); if (pop<0) min=min-pop;//If negative, increase the min value } public int top() { long top=stack.peek(); if (top>0){ return (int)(top+min); }else{ return (int)(min); } } public int getMin() { return (int)min; } }
最后一种只用一个stack的解法,就是基于我所说的倒数第二个元素存储最小值的改进,这里最小值改进为用单独的min来存储,如果最小值被更新,则把该最小值压栈,具体见Java代码:
class MinStack { int min=Integer.MAX_VALUE; Stackstack = new Stack (); public void push(int x) { // only push the old minimum value when the current // minimum value changes after pushing the new value x if(x <= min){ stack.push(min); min=x; } stack.push(x); } public void pop() { // if pop operation could result in the changing of the current minimum value, // pop twice and change the current minimum value to the last minimum value. if(stack.peek()==min) { stack.pop(); min=stack.peek(); stack.pop(); }else{ stack.pop(); } if(stack.empty()){ min=Integer.MAX_VALUE; } } public int top() { return stack.peek(); } public int getMin() { return min; } }
设计题思路五花八门,还是很有意思哒~