来源Leetcode第155题最小栈
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x) — 将元素 x 推入栈中。
- pop() — 删除栈顶的元素。
- top() — 获取栈顶元素。
- getMin() — 检索栈中的最小元素。
示例:
1 2 3 4 5 6 7 8
| MinStack minStack = new MinStack() minStack.push(-2) minStack.push(0) minStack.push(-3) minStack.getMin() minStack.pop() minStack.top() minStack.getMin()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
---
#### 双栈
利用两个栈,一个数据栈,一个保存所有最小值栈实现。
当最小值栈非空时,元素直接入栈,否则需要比较栈顶元素与入栈元素大小,如果小于等于,则入栈
代码如下:
```JAVA class MinStack {
private Stack<Integer> stack; private Stack<Integer> miniStack;
public MinStack() { stack = new Stack<>(); miniStack = new Stack<>(); }
public void push(int x) { stack.push(x); if(!miniStack.empty()){ if(x <= miniStack.peek()){ miniStack.push(x); } }else miniStack.push(x); }
public void pop() { int pop = stack.pop();
if(pop == miniStack.peek()) miniStack.pop(); }
public int top() { return stack.peek(); }
public int getMin() { return miniStack.peek(); } }
|
单栈
当有更小的值来的时候,我们只需要把之前的最小值入栈,当前更小的值再入栈即可。当这个最小值要出栈的时候,下一个值便是之前的最小值了。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| 入栈 3 | | min = 3 | | |_3_| stack
入栈 5 | | min = 3 | 5 | |_3_| stack
入栈 2 | 2 | min = 2? | 5 | |_3_| stack
入栈 2 ,同时将之前的 min 值 3 入栈,再把 2 入栈,同时更新 min = 2 | 2 | min = 2 | 3 | | 5 | |_3_| stack
入栈 6 | 6 | min = 2 | 2 | | 3 | | 5 | |_3_| stack
出栈 6 | 2 | min = 2 | 3 | | 5 | |_3_| stack
出栈 2 | 2 | min = 2 | 3 | | 5 | |_3_| stack
出栈 2 | | min = 3 | 5 | |_3_| stack
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class MinStack { int min = Integer.MAX_VALUE; Stack<Integer> stack = new Stack<Integer>(); public void push(int x) { if(x <= min){ stack.push(min); min=x; } stack.push(x); }
public void pop() { if(stack.pop() == min) { min=stack.pop(); } }
public int top() { return stack.peek(); }
public int getMin() { return min; } }
|
差值栈
用一个 min 变量保存最小值。只不过栈里边我们不去保存原来的值,而是去存储入栈的值和最小值的差值。然后得到之前的最小值的话,我们就可以通过 min 值和栈顶元素得到,举个例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| 入栈 3,存入 3 - 3 = 0 | | min = 3 | | |_0_| stack
入栈 5,存入 5 - 3 = 2 | | min = 3 | 2 | |_0_| stack
入栈 2,因为出现了更小的数,所以我们会存入一个负数,这里很关键 也就是存入 2 - 3 = -1, 并且更新 min = 2 对于之前的 min 值 3, 我们只需要用更新后的 min - 栈顶元素 -1 就可以得到 | -1| min = 2 | 5 | |_3_| stack
入栈 6,存入 6 - 2 = 4 | 4 | min = 2 | -1| | 5 | |_3_| stack
出栈,返回的值就是栈顶元素 4 加上 min,就是 6 | | min = 2 | -1| | 5 | |_3_| stack
出栈,此时栈顶元素是负数,说明之前对 min 值进行了更新。 入栈元素 - min = 栈顶元素,入栈元素其实就是当前的 min 值 2 所以更新前的 min 就等于入栈元素 2 - 栈顶元素(-1) = 3 | | min = 3 | 5 | |_3_| stack
|
再理一下上边的思路,我们每次存入的是 原来值 - 当前最小值
。
当原来值大于等于当前最小值的时候,我们存入的肯定就是非负数,所以出栈的时候就是 栈中的值 + 当前最小值
。
当原来值小于当前最小值的时候,我们存入的肯定就是负值,此时的值我们不入栈,用 min
保存起来,同时将差值入栈。
当后续如果出栈元素是负数的时候,那么要出栈的元素其实就是 min
。此外之前的 min
值,我们可以通过栈顶的值和当前 min
值进行还原,就是用 min
减去栈顶元素即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class MinStack { long min; Stack<Long> stack;
public MinStack(){ stack=new Stack<>(); }
public void push(int x) { if (stack.isEmpty()) { min = x; stack.push(x - min); } else { stack.push(x - min); if (x < min){ min = x; } } }
public void pop() { if (stack.isEmpty()) return;
long pop = stack.pop(); if (pop < 0) { min = min - pop; }
}
public int top() { long top = stack.peek(); if (top < 0) { return (int) (min); } else { return (int) (top + min); } }
public int getMin() { return (int) min; } }
|
链表
再分享一个有趣的解法,参考 这里 。
回到最初的疑虑,我们要不要用 java
提供的 stack
。如果不用的话,可以怎么做的?
直接用一个链表即可实现栈的基本功能,那么最小值该怎么得到呢?我们可以在 Node
节点中增加一个 min
字段,这样的话每次加入一个节点的时候,我们同时只要确定它的 min
值即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 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{ 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; } }
|