30 包含min函数的栈
1 题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
示例
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.
2 题目分析
解法一:用辅助栈的思想,即创建一个辅助栈用来存储当前情况下的最小值,初始辅助栈里存储Integer.MAX_VALUE,之后每次主栈入栈时都判断当前元素元辅助栈栈顶元素的大小,将较小者入栈,出栈时两个栈一同出栈,这样求最小元素时只需返回辅助栈的栈顶元素即可,这也是最常用的想法,一般面试官到此就会满足,但不排除要求更高的面试官会问不使用辅助栈如何解决?看解法二!
解法二:用差值法的思想,我们先每次入栈时将栈顶元素和min值做差后的结果加入栈中,有个很巧妙的结论:
-
入栈时:当差值大于等于0时,min值不变;否则将min值变为当前元素。
-
出栈时:当要出栈元素大于等于0时,min值还是不变;否则说明当前元素出栈后min值会变,具体变成什么?答案是当前的min-栈顶元素(因为栈顶元素是负数,因此会变大)画个图就容易理解了:
解法三:上述两种解法已经足够应对面试了,这里的第三种解法了解即可,其实也是解法一的变种,在入栈的时候当min值变化时先将原来的min加入的栈再入栈,这样出栈时如果栈顶元素等于min值时就连续出两次栈,将第二次的值更新为min。只不过这样要严格判断只要入栈时的min小于或者等于min时就入两次栈!
3 代码
- 解法一:
/*
包含min函数的栈,用O(1)的时间复杂度实现,快速想到用辅助栈去存储最小元素,初始时辅助栈中加入最大值,即每次主栈入栈时判断当前元素与辅助栈中栈顶元素相比,然后就小的加入辅助栈中。
这样求最小元素只需返回辅助栈的栈顶元素即可,出栈操作时辅助栈和主栈同时出栈即可。
*/
static class MinStack {
Deque<Integer> stack;
Deque<Integer> min;
/** initialize your data structure here. */
public MinStack() {
stack = new LinkedList<>();
min = new LinkedList<>();
min.push(Integer.MAX_VALUE);
}
public void push(int x) {
stack.push(x);
min.push(Math.min(min.peek(), x));
}
public void pop() {
stack.pop();
min.pop();
}
public int top() {
return stack.peek();
}
public int min() {
return min.peek();
}
}
- 解法二:
/*
栈中永远存储当前值和min值的差值,这样的目的是判断当前值和min值哪一个是当前的真正最小值,也能根据公式去取最小值:
1. 如果差值是大于等于0的,说明当前真正的最小值是后者,入栈时将差值加入,出栈后min值不用更新
2. 如果差值是小于0的,说明当前的最小值是前者,那么入栈时加入到就是负数,更新min值为当前值,出栈由于出的就是最小值因此需要还原上一个min值,这时好处就是我们可以用的当前min减去栈顶元素来回溯的之前的min
注意:差值法用Java容易造成越界,因此需要改成long类型
*/
static class MinStack {
Deque<Long> stack;
long min;
/** initialize your data structure here. */
public MinStack2() {
stack = new LinkedList<>();
min = Integer.MAX_VALUE;
}
public void push(int x) {
/*if (x < min) {
// 如果x小于min,将差值入栈,更新min
stack.push(x - min);
min = x;
} else {
// 直接将差值入栈
stack.push(x - min);
}*/
stack.push(x - min);
if (x < min) min = x;
}
public void pop() {
if (stack.isEmpty()) return;
// 出栈时,判断当前栈顶元素是不是负的
long val = stack.pop();
if (val < 0) {
// 回溯min
min = min - val;
}
}
public int top() {
long top = stack.peek();
if (top > 0) return (int) (top + min);
else return (int) min; // 当前min就是出栈的元素
}
public int min() {
return (int) min;
}
}
- 解法三:
/*
思路:其实很多同学第一个想到的并不是辅助栈,而是用一个变量去存储最小值,这样求最小值的时候直接返回该变量就行了,但是有一个问题就是如果出栈的元素就是
最小值的时候我们不知道第二小的元素是谁,以此类推,第二小的出栈我们也不知道第三小的元素是谁?那么有什么方法去保存这些最小,第二小这些元素呢,可以在放入
最小值元素前就把上次的最小值先放入栈中保存,这样最小值出栈后,再出栈的元素就是次小的元素了。多出一次栈就能解决问题,代码就简单了。
*/
static class MinStack {
Deque<Integer> stack;
int min;
/** initialize your data structure here. */
public MinStack1() {
stack = new LinkedList<>();
min = Integer.MAX_VALUE;
}
public void push(int x) {
// 入栈之前先判断当前元素与min的大小,如果小或等的话则先把上次的min放进去,然后更新min值
if (x <= min) {
stack.push(min);
min = x;
}
stack.push(x);
}
public void pop() {
// 出栈时也先判断栈顶元素和min的关系,如果相等则连续出两次栈
if (!stack.isEmpty() && stack.pop() == min) {
min = stack.pop();
}
}
public int top() {
return stack.peek();
}
public int min() {
return min;
}
}
参考LeetCode大神:windliangL6题解
4 智商被碾压的解法
定义一个链表结构来实现栈的基本功能。
class MinStack {
class Node{
int value;
int min;
Node next;
Node(int x, int min){
this.value=x;
this.min=min;
next = null;
}
}
Node head;
//每次加入的节点放到头部
public void push(int x) {
if(null==head){
head = new Node(x,x);
}else{
//当前值和之前头结点的最小值较小的做为当前的 min
Node n = new Node(x, Math.min(x,head.min));
n.next=head;
head=n;
}
}
public void pop() {
if(head!=null)
head =head.next;
}
public int top() {
if(head!=null)
return head.value;
return -1;
}
public int getMin() {
if(null!=head)
return head.min;
return -1;
}
}
public int top() {
if(head!=null)
return head.value;
return -1;
}
public int getMin() {
if(null!=head)
return head.min;
return -1;
}
}