题目
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
- 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.
图解记忆:
错误代码:
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的回答 - 知乎