Nameless Site

But one day, you will stand before its decrepit gate,without really knowing why.

0%

最小栈

来源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(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
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 {

/** initialize your data structure here. */

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();

//弹出的是负值,要更新 min
if (pop < 0) {
min = min - pop;
}

}

public int top() {
long top = stack.peek();
//负数的话,出栈的值保存在 min 中
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{
//当前值和之前头结点的最小值较小的做为当前的 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;
}
}