0.简介
本文章将介绍最小栈原理和实现。
1.最小栈
栈本身有push,pop操作,现在需要添加一个min操作,min操作就是求得栈中最小元素,要求时间复杂度是O(1)。
2.分析
其实求栈中最小值本身不是难题,这里难点在于时间复杂度是O(1),当找min值的时候,要求立刻就找到,这个就要追寻栈的一些本质算法。从数括号深度来看吧。
说有这么一段代码。
int fun()
{//作用域1
int a;
int b;
{//作用域2
int a;
int b;
{//作用域3
int a;
int b;
a = a + b; //语句3
}
a = a + b; //语句2
}
a = a + b; //语句1
return 0;
}
这段代码本身没什么意义,随意写的一段,我们要关注的是每个变量的作用域,我们很清楚的知道,语句3用的a、b变量一定是作用域3中的,因为距离语句3最近,我们可以认为是最近声明的变量在最上面。当退出作用域3来到作用域2的时候,语句2用的是作用域2中的a、b,这个过程就有些类似栈。这里所说的说的作用域的界限就是大括号。看下面的数字。
5
{ min = 5 //作用域1
6
3
2
{ min = 2 //作用域2
8
9
7
2
1
{ min = 1 //作用域3
6
7
}
}
}
此时将大括号作用域上面增加了一个变量,叫做最小值,用这个最小值作为作用域界限,加入数字,从最上边往栈a中输入,当遇到第一个最小的值5,记录这个值,然后此时这个值就是目前栈中最小值,继续输入6、3、2,2比之前的5还要小,所以将2也存储下来,最后遇到比2还小的值1,那么把1也存贮,5、2、1这三个数字实际上就形成了三个不同的作用域,在作用域3的时候,栈中最小值是1,将7、6、1都出栈,此时作用域3消失,来到了作用域2,此时2是最小值,因为刚刚记录过了,我们将5、2、1这三个数一次存入了另一个栈b中了,所以将1出栈之后,栈顶就是2了,继续将栈a内容出栈,当出栈到2的时候,与栈b中记录的最小值一样,就相当于到了大括号,此时作用域2也消失,回到了作用域1。
看了上面一大段话,可能已经迷糊了,总结一下就是,原本的作用域是用大括号这个符号区分的,现在用当前最小值来当做作用域划分符号。用大括号区分的时候,因为大括号和数字不同,所以我们很轻松能知道什么是大括号,什么是数字,即便是都存储在一个栈里也能区分开。现在用最小值来划分作用域,因为最小值也是数字,和其余的值没有明显区别,所以我们将当前最小值存储在另外一个栈b中,也就是当前输入到栈a的值x,如果比栈b的top值y还要小,说明一个此时遇到新的作用域了,那么就将x存储栈b中,当从栈a出栈的时候,要比较出栈的值p是不是和栈b的top值q相等,是的话说明以p为界限的这个作用域已经从栈中彻底消失,退回到了上一个作用域了。总结也说了这么多。。。
那就再简化一下,栈可以保存并访问离当前状态最近的东西。
3.实现
class Solution {
public:
stack<int>s;
stack<int>m;
void push(int value) {
s.push(value);
if(m.empty())
m.push(value);
if(m.top()>value)
m.push(value);
}
void pop() {
if(s.top() == m.top())
m.pop();
s.pop();
}
int top() {
return s.top();
}
int min() {
return m.top();
}
};