题目
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x) —— 将元素 x 推入栈中。
- pop() —— 删除栈顶的元素。
- top() —— 获取栈顶元素。
- getMin() —— 检索栈中的最小元素。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
|
提示:
- pop、top 和 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
|
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
s.push(x);
bool inserted = false;
for (auto itr = l.begin(); itr != l.end(); itr++) {
if(x < *itr){
l.insert(itr, x);
inserted = true;
break;
}
}
if (!inserted) {
l.push_back(x);
}
}
void pop() {
int value = s.top();
for (auto itr = l.begin(); itr != l.end(); itr++) {
if (*itr == value) {
l.erase(itr);
break;
}
}
s.pop();
}
int top() {
return s.top();
}
int getMin() {
return *l.begin();
}
private:
stack<int> s;
list<int> l;
};
|
这里的队列没有使用标准库中的队列,而是用list模拟了一个队列,在插入元素时,将元素插入到list中,而在pop时,也需要将pop出的元素从list中移除。整体来说,push和pop的复杂度是O(N),而getMin的复杂度是O(1),满足要求,在提交代码后也可测试通过。
最后还是查看了讨论,常用的思路是使用辅助栈。使用辅助栈的实现中,push和pop的时间复杂度均为O(1),对应的实现如下:
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
53
54
55
56
57
58
59
60
61
62
63
|
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
s.push(x);
if (l.empty())
l.push(x);
else {
if (x <= l.top())
l.push(x);
}
}
void pop() {
if (s.top() == l.top())
l.pop();
s.pop();
}
int top() {
return s.top();
}
int getMin() {
return l.top();
}
private:
stack<int> s;
stack<int> l;
};
// 或是下面这种实现
class MinStack {
stack<int> x_stack;
stack<int> min_stack;
public:
MinStack() {
min_stack.push(INT_MAX);
}
void push(int x) {
x_stack.push(x);
min_stack.push(min(min_stack.top(), x));
}
void pop() {
x_stack.pop();
min_stack.pop();
}
int top() {
return x_stack.top();
}
int getMin() {
return min_stack.top();
}
};
|
上面两种实现中都是使用的辅助栈,但在细节上略有些不同,下面那种实现可能会更为广泛,简单来说就是每push一个元素,都把当前栈中的最小元素push到辅助栈中,当pop时一并pop出辅助栈的元素,辅助栈的栈顶一直存储当前栈的最小值。
而在上面那种实现中,理解起来会稍微有些困难,它并不是每当push就push最小值,而是只有当push的元素小于当前的最小元素时才进行push,在pop时也同样,只有当pop的元素与辅助栈的栈顶一致时才pop出。
两种实现中,下面那种会更加人理解和记忆。总的来说,我们需要一个结构实时的存储当前栈的最小元素,并随着栈的push和pop不断的更新当前栈的最小元素。