Min Stack

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的栈顶存的不再是最小元素:

例如压入以下数后:
xi:    3 2 1 2 1 
trackMin: 3 2 1

此时如果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 Stack<Element> stack = 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;
    Stack<Long> stack;

    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<min) min=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;
    Stack<Integer> stack = new Stack<Integer>();
    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;
    }
}

设计题思路五花八门,还是很有意思哒~

原文地址:https://www.cnblogs.com/sherylwang/p/5470096.html