自己实现的LeetCode相关题解代码库:https://github.com/Yuri0314/Leetcode
设计一个支持push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
push(x)
—— 将元素 x 推入栈中。pop()
—— 删除栈顶的元素。top()
—— 获取栈顶元素。getMin()
—— 检索栈中的最小元素。示例:
输入:
["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
操作总是在 非空栈 上调用。题目所要求实现的push、pop和top操作,普通的栈都提供,并且均可以在O(1)的时间复杂度内做到,因此本题的难点在于如何在常数时间内实现getMin操作,即获取栈内元素最小值。
仔细分析栈的特点不难发现一共需要考虑4种情境:
入栈元素值比现有最小元素小
入栈元素值比现有最小元素大
出栈元素是当前最小元素
出栈元素不是当前最小元素
可以看到,上面的2、4情境对当前栈内最小值无影响,所以只需考虑入栈和出栈最小值的情境即可。可以使用两种解法,下面依次进行说明。
除了保存数据的栈之外,可以使用一个额外的辅助栈来存储一些辅助数据。
该辅助栈的栈顶要求保存的始终是当前栈元素中的最小元素。为了实现这一要求,针对上面的4种情境,辅助栈的对应做法是:
总结就是,数据栈当作普通栈来使用,而辅助栈只有当入栈元素比其栈顶元素小时再入栈,当数据栈出栈元素等于辅助栈栈顶元素时(即出栈当前最小元素)弹出辅助栈栈顶元素。
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minStack;
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<Integer>();
minStack = new Stack<Integer>();
}
public void push(int x) {
stack.push(x);
if (minStack.empty())
minStack.push(x);
else if (x <= minStack.peek())
minStack.push(x);
}
public void pop() {
int item = stack.pop();
if (item == minStack.peek())
minStack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
除了使用一个辅助栈来帮助实现,还可以重新定义一个栈结构,其栈中每一个元素是自定义的一个节点,节点中存储了当前元素的值和当前元素作为栈顶元素时的最小值。
每当插入一个新元素时,比较该元素值和目前栈顶结点中最小值,将其更小的那个值作为该插入新结点的最小值,然后将生成的新节点入栈。这样无论入栈还是出栈,当前栈顶保存结点都有存储对应的当前最小值,直接取出即可。
class MinStack {
private class Node {
int val;
int min;
Node next;
Node(int val, int min) {
this(val, min, null);
}
Node (int val, int min, Node next) {
this.val = val;
this.min = min;
this.next = next;
}
}
private Node head;
/** initialize your data structure here. */
public MinStack() {
head = null;
}
public void push(int x) {
if (head == null)
head = new Node(x, x);
else
head = new Node(x, Math.min(x, head.min), head);
}
public void pop() {
head = head.next;
}
public int top() {
return head.val;
}
public int getMin() {
return head.min;
}
}
与最小栈类似,要求设计一个队列,其需要支持在O(1)的时间满足:出队、入队和求最大元素的操作。
从最小栈的问题类推过来,同样有4种情境:
与栈的结构不同,队列是双端操作,所以上述对最小栈的实现中的第二种方法无法适用改写到该问题中。
本题可借用最小栈的辅助栈解法,使用一个数据队列作为普通队列使用,一个辅助队列来实现O(1)时间获取最大元素,其中的元素值保持单调性。
在这种实现下,要获取队列最大值只需要获取辅助队列队头元素值即可。
public class MyQueue {
private Deque<Integer> queue = new LinkedList<Integer>();
private Deque<Integer> monoQueue = new LinkedList<Integer>();
public void push(int x) {
queue.addLast(x);
while (!monoQueue.isEmpty() && x > monoQueue.getLast())
monoQueue.removeLast();
monoQueue.addLast(x);
}
public int pop() {
int x = queue.removeFirst();
if (x == monoQueue.getFirst())
monoQueue.removeFirst();
return x;
}
public int getMax() {
return monoQueue.getFirst();
}
}