前言
在设计一个能够支持常数时间内检索到最小元素的栈(MinStack)时,我们需要考虑如何在执行 push、pop、top 和 getMin 操作时保持效率。这个问题的关键在于如何在保持栈操作的常数时间复杂度下,实现获取栈中最小元素的操作。
题目描述
请你设计一个 最小栈 。它提供 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
来源力扣:LCR 147. 最小栈
实现 MinStack 类:
MinStack()初始化堆栈对象。void push(int val)将元素val推入堆栈。void pop()删除堆栈顶部的元素。int top()获取堆栈顶部的元素。
提示
-231 <= val <= 231 - 1pop、top和getMin操作总是在 非空栈 上调用push、pop、top和getMin最多被调用3 * 104次int getMin()获取堆栈中的最小元素。
简单示例
示例 1:
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[2],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,2,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(2);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 2.
minStack.getMin(); --> 返回 -2.
解题思路
-
数据结构选择:
- 使用两个数组
stack和min。stack用于存储正常的栈数据,min用于存储最小元素的栈。
- 使用两个数组
-
push 操作:
- 在
push操作时,先将元素x推入stack中。 - 如果
min栈为空,或者新推入的元素x小于等于min栈的栈顶元素(即当前的最小值),则将x也推入min栈中。这样保证min栈的栈顶始终是当前stack中的最小元素。
- 在
-
pop 操作:
- 在
pop操作时,从stack中弹出栈顶元素。 - 同时检查该弹出的元素是否等于
min栈的栈顶元素,如果是,则也从min栈中弹出栈顶元素。这样保证min栈的栈顶始终是当前stack中剩余元素的最小值。
- 在
-
top 操作:
top操作直接返回stack栈顶元素,即不会对min栈产生影响。
-
getMin 操作:
getMin操作直接返回min栈的栈顶元素,该元素即为当前stack中的最小值。
-
完整代码:
var MinStack = function() {
this.stack = []; // 正常的栈
this.min = []; // 最小元素栈
};
MinStack.prototype.push = function(x) {
// 如果最小栈为空,或者新加入的元素比最小栈的栈顶元素小或者等于,才将x推入min栈
if (!this.min.length || x <= this.min[this.min.length - 1]) {
this.min.push(x);
}
// 将x推入正常栈
this.stack.push(x);
};
MinStack.prototype.pop = function() {
// 弹出正常栈的栈顶元素
const popped = this.stack.pop();
// 如果弹出的元素等于最小栈的栈顶元素,也弹出最小栈的栈顶元素
if (popped === this.min[this.min.length - 1]) {
this.min.pop();
}
};
MinStack.prototype.top = function() {
// 返回正常栈的栈顶元素
return this.stack[this.stack.length - 1];
};
MinStack.prototype.getMin = function() {
// 返回最小栈的栈顶元素,即为当前正常栈中的最小值
return this.min[this.min.length - 1];
};
时间复杂度分析
-
push(x) :
- 在正常栈
stack上执行的操作是常数时间复杂度:O(1). - 在最小栈
min上的操作也是常数时间复杂度,因为只有在x是当前最小值时才会更新min栈:O(1).
因此,
push(x)的总体时间复杂度是O(1). - 在正常栈
-
pop() :
- 从正常栈
stack弹出栈顶元素操作是常数时间复杂度:O(1). - 如果弹出的元素是当前最小值,那么从最小栈
min中也要弹出栈顶元素,同样是常数时间复杂度:O(1).
所以,
pop()的总体时间复杂度是O(1). - 从正常栈
-
top() :
- 返回正常栈
stack的栈顶元素操作是常数时间复杂度:O(1).
因此,
top()的时间复杂度是O(1). - 返回正常栈
-
getMin() :
- 返回最小栈
min的栈顶元素操作也是常数时间复杂度:O(1).
所以,
getMin()的时间复杂度是O(1). - 返回最小栈
空间复杂度分析
-
正常栈
stack:- 存储所有
push进来的元素,空间复杂度取决于stack的大小,即为O(n),其中n是执行push操作的次数。
- 存储所有
-
最小栈
min:- 最坏情况下,如果元素是递减
push进来的,那么min栈会和stack栈一样大,空间复杂度也是O(n),其中n是执行push操作的次数。
- 最坏情况下,如果元素是递减
小结
通过使用两个栈来实现 MinStack,我们成功地实现了在常数时间内检索最小元素的需求。这种设计在保持了操作的高效性的同时,也符合了题目的要求和预期的时间复杂度。使用栈这种数据结构的特性,我们能够简洁而有效地解决了这一问题,确保了数据结构的稳定性和操作的高效性。