LeetCode 155 :最小栈 - 辅助栈

393 阅读3分钟

题目

设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。

  • push(x) —— 将元素 x 推入栈中。
  • pop() —— 删除栈顶的元素。
  • top() —— 获取栈顶元素。
  • getMin() —— 检索栈中的最小元素。

题解

这题跟以前做过的题不太一样,题目读了两遍没看懂意思。

首先我们要明白一个最小栈的意思。题目中给出的示例函数是说:

/**
 * initialize your data structure here.
 */
var MinStack = function() {
    
};

他其实是让我们去设计一个数据结构叫MinStack,这个最小栈有个什么功能呢,它能在常数时间内检索到最小元素。这就是它和普通栈的区别。

明白了这一点就不难了,现在我们就是要把它设计出来。

我觉得官方的思路就很好理解:

要做出这道题目,首先要理解栈结构先进后出的性质。

对于栈来说,如果一个元素 a 在入栈时,栈里有其它的元素 b, c, d,那么无论这个栈在之后经历了什么操作,只要 a 在栈中,b, c, d 就一定在栈中,因为在 a 被弹出之前,b, c, d 不会被弹出。

因此,在操作过程中的任意一个时刻,只要栈顶的元素是 a,那么我们就可以确定栈里面现在的元素一定是 a, b, c, d。

那么,我们可以在每个元素 a 入栈(push)时把当前栈的最小值 m 存储起来。在这之后无论何时,如果栈顶元素是 a,我们就可以直接返回存储的最小值 m.

图解记忆:

155.gif

错误代码:

MinStack.prototype.push = function(val) {
    this.stack.push(val);
    //比minStack的栈顶元素小就要入栈
    if(val<this.minStack[this.minStack.length-1]){
        this.minStack.push(val);
    }
};

MinStack.prototype.pop = function() {
    if(this.top()==this.minStack[this.minStack.length-1]){
        this.minStack.pop();
    }
    this.stack.pop();
};

最开始在写的时候犯了一个很致命的错误,就是两个栈没有同步,我将push设定为只有在比minStack的栈顶元素小才要入栈。那么对于下面这种示例就会报错:

输入:["MinStack","push","push","push","getMin","pop","top","getMin"]
	 [[],[0],[1],[0],[],[],[],[]]
输出:[null,null,null,null,0,null,1,Infinity]
预期:[null,null,null,null,0,null,1,0]

这是因为,在我们push完哦0,1,0后,minStack中的数只有Infinity和一个0,这样导致后面再pop一次后,minStack就一个0都没有了,但是stack中还有一个,这时就造成了双栈不同步。

所以正确的解法是,不管什么时候,都要入栈一个最小元素,即使和刚才入栈的元素相同。

正解:

var MinStack = function() {
    this.stack = [];
    this.minStack = [Infinity];
};


MinStack.prototype.push = function(val) {
    this.stack.push(val);
    //比minStack的栈顶元素小就要入栈
    this.minStack.push(Math.min(this.minStack[this.minStack.length - 1], val));
};


MinStack.prototype.pop = function() {
    this.minStack.pop();
    this.stack.pop();
};


MinStack.prototype.top = function() {
    return this.stack[this.stack.length-1];
};


MinStack.prototype.getMin = function() {
    return this.minStack[this.minStack.length-1];
};

体会

不管是简单的题还是难题,都有一些坑,我们的思路一旦找的不对,就很容易卡死,所以每步的思考一定要用心,看完官方解答后即使懂了也要按自己再做一遍,如果出错了更好,这样就能更清楚的理解为什么这里要这样设计,记忆更加深刻,不断试错也是学习的过程。

最后推一篇个人认为还不错的刷算法指南:程序员必须掌握哪些算法? - 李rumor的回答 - 知乎