变量命名说明: mins (min stack缩写) data (数据栈)
剑指offer经典解法 很朴素、直接:辅助栈mins存储每次push时的最小值;pop时辅助栈mins和数据栈data两栈同步pop。
时间复杂度O(1),空间复杂度(n)。
人生就是一个不断追求极致的过程,生活就是艺术——代码更是艺术。存在一个不太合理的地方——mins栈会保留大量冗余的最小值。如2、1、2、3、4、5、6顺次入栈:
讨论:从图1 我们可以看到,mins保留了大量冗余的最小值。这也许是可以避免的(之所以这么说,万一没有办法解决这个问题,也就不可避免了,嘿嘿)。
mins不允许出现冗余,所有值都顺次只允许出现一次不就行了?但这带来了新问题。细想:该思路的要求是?push时进行判断,如果要入栈的元素比当前最小值大,mins保持不变;当push一个和当前最小值相等的元素,仍需要入栈;否则,mins栈中的栈顶元素pop出去之后,data栈中还可能存在最小值元素吗?这种情况是可以发生的,但此时mins却已经不含最小值元素,min()函数直接返回mins的栈顶元素,返回就不正确了!!!所以在这种思路的引导下,冗余是不可缺少的、是必要的。
本篇博客立足于如何优化剑指offer的解法。
mins出现大量冗余的根源是什么呢?pop时辅助栈mins和数据栈data两栈会同步pop——有没有一种方法,使之pop时,mins到底是否执行pop额外判断一下;毫无疑问,数据栈data肯定还是要执行pop。
革新:mins中存放最小值在data中的索引(先来后到)。如果push一个和最小值相同的元素,mins不需要改变;pop时,如果data将要除去的元素的索引不是mins栈顶元素,mins不执行出栈操作。
获得栈中最小元素的min()函数的实现思路:拿到mins栈顶元素作为索引,再去data中找到相应的元素作为最小值(按图索骥大概就是这个意思吧)——由此我们知道,data应该采用vector<int>,因为需要满足随机存取的特点。
C++代码实现如下
class Solution {
public:
void push(int value) {
data.push_back(value);
if(mins.empty())
mins.push(0); //初始化mins栈
else{
if(value < min())
mins.push(data.size() - 1); //压入更新后最小值的索引
}
}
void pop() {
if(!data.empty()){
if(data.size() - 1 == mins.top()) //pop出的元素索引如果是mins栈顶元素,执行出栈
mins.pop();
data.pop_back(); //删除最后一个元素 后进先出
}
}
int top() {
return data[data.size() - 1];
}
int min() {
return data[mins.top()];
}
private:
vector<int>data;
stack<int>mins;
};