题目
标题和出处
标题:最小栈
出处:155. 最小栈
难度
4 级
题目描述
要求
设计一个支持入栈、出栈、查看栈顶元素操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack \texttt{MinStack} MinStack 类:
- MinStack() \texttt{MinStack()} MinStack() 初始化栈对象。
- void push(val) \texttt{void push(val)} void push(val) 将元素 val \texttt{val} val 入栈。
- void pop() \texttt{void pop()} void pop() 删除栈顶的元素。
- int top() \texttt{int top()} int top() 获取栈顶元素。
- int getMin() \texttt{int getMin()} int getMin() 检索栈内的最小元素。
示例
示例 1:
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
\texttt{["MinStack","push","push","push","getMin","pop","top","getMin"]}
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
\texttt{[[],[-2],[0],[-3],[],[],[],[]]}
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
\texttt{[null,null,null,null,-3,null,0,-2]}
[null,null,null,null,-3,null,0,-2]
解释:
MinStack
minStack
=
new
MinStack();
\texttt{MinStack minStack = new MinStack();}
MinStack minStack = new MinStack();
minStack.push(-2);
\texttt{minStack.push(-2);}
minStack.push(-2);
minStack.push(0);
\texttt{minStack.push(0);}
minStack.push(0);
minStack.push(-3);
\texttt{minStack.push(-3);}
minStack.push(-3);
minStack.getMin();
\texttt{minStack.getMin();}
minStack.getMin(); // 返回
-3
\texttt{-3}
-3
minStack.pop();
\texttt{minStack.pop();}
minStack.pop();
minStack.top();
\texttt{minStack.top();}
minStack.top(); // 返回
0
\texttt{0}
0
minStack.getMin();
\texttt{minStack.getMin();}
minStack.getMin(); // 返回
-2
\texttt{-2}
-2
数据范围
- -2 31 ≤ val ≤ 2 31 − 1 \texttt{-2}^\texttt{31} \le \texttt{val} \le \texttt{2}^\texttt{31} - \texttt{1} -231≤val≤231−1
- 方法 pop \texttt{pop} pop、 top \texttt{top} top 和 getMin \texttt{getMin} getMin 总是在非空栈上调用
- 最多调用 3 × 10 4 \texttt{3} \times \texttt{10}^\texttt{4} 3×104 次 push \texttt{push} push、 pop \texttt{pop} pop、 top \texttt{top} top 和 getMin \texttt{getMin} getMin
解法
思路和算法
最小栈需要支持常规栈的操作,另外还要在常数时间内检索到最小元素。为了支持常规栈的操作,需要维护一个常规栈。为了在常数时间内检索到最小元素,需要维护一个最小元素栈,最小元素栈的栈顶元素为最小元素。常规栈和最小元素栈分别为 stack \textit{stack} stack 和 minStack \textit{minStack} minStack。
对于 push \textit{push} push 方法,进行如下操作:
-
将元素 val \textit{val} val 入栈 stack \textit{stack} stack;
-
如果 minStack \textit{minStack} minStack 为空或者 val \textit{val} val 小于等于 minStack \textit{minStack} minStack 的栈顶元素,则 val \textit{val} val 是最小元素,将 val \textit{val} val 入栈 minStack \textit{minStack} minStack。
对于 pop \textit{pop} pop 方法,进行如下操作:
-
将 stack \textit{stack} stack 的栈顶元素出栈,记为 val \textit{val} val;
-
如果 val \textit{val} val 和 minStack \textit{minStack} minStack 的栈顶元素相等,则出栈的元素是最小元素,将 minStack \textit{minStack} minStack 的栈顶元素出栈。
对于 top \textit{top} top 方法,返回 stack \textit{stack} stack 的栈顶元素。
对于 getMin \textit{getMin} getMin 方法,返回 minStack \textit{minStack} minStack 的栈顶元素。
根据上述操作, minStack \textit{minStack} minStack 的元素个数总是少于或等于 stack \textit{stack} stack 的元素个数,且 minStack \textit{minStack} minStack 是单调栈,栈底到栈顶的元素非严格单调递减。
由于当每次入栈的元素是最小元素时,该元素都会入栈 minStack \textit{minStack} minStack,且 minStack \textit{minStack} minStack 具有单调性,因此在只有 push \textit{push} push 操作的情况下, minStack \textit{minStack} minStack 的栈顶元素一定是最小元素, getMin \textit{getMin} getMin 方法返回的一定是最小元素。
在有 pop \textit{pop} pop 操作的情况下, minStack \textit{minStack} minStack 的栈顶元素也一定是最小元素,理由如下。
根据 push \textit{push} push 和 pop \textit{pop} pop 方法的实现可知, minStack \textit{minStack} minStack 中的每个元素都在 stack \textit{stack} stack 中有一个对应元素,对应元素的含义是,这两个元素(分别属于两个栈)同时入栈和同时出栈。当一个元素从 minStack \textit{minStack} minStack 出栈时,该元素在 stack \textit{stack} stack 中的对应元素同时出栈,此时两个栈内的元素仍然符合最小栈的性质。
下图为题中示例的三次入栈操作的过程。
下图为题中示例的一次出栈操作的过程。由于 stack \textit{stack} stack 的出栈元素 − 3 -3 −3 和 minStack \textit{minStack} minStack 的栈顶元素相等,因此也将 minStack \textit{minStack} minStack 的栈顶元素出栈。
代码
class MinStack {
Deque<Integer> stack;
Deque<Integer> minStack;
public MinStack() {
stack = new ArrayDeque<Integer>();
minStack = new ArrayDeque<Integer>();
}
public void push(int val) {
stack.push(val);
if (minStack.isEmpty() || val <= minStack.peek()) {
minStack.push(val);
}
}
public void pop() {
int val = stack.pop();
if (minStack.peek() == val) {
minStack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
复杂度分析
-
时间复杂度:构造方法和每一项操作的时间复杂度都是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是元素个数。空间复杂度主要取决于栈空间,常规栈和最小元素栈的空间都是 O ( n ) O(n) O(n)。