单调栈 & 单调队列
单调栈(Monotonic Stack)
单调栈:本质上还是一个先进后出的栈结构,但是在将元素压入栈中时,需要保持栈内所有元素具有单调性(单调递增/单调递减)
单调栈的核心功能需求:添加元素时要保证栈中的数据呈现单调性。
public class MonotonicStack {
Stack<Integer> stack = new Stack<>();
// 保证单调递减
public void push(int num) {
while (!stack.isEmpty() && stack.peek() < num) {
stack.pop();
}
stack.push(num);
}
// 弹出栈顶元素
public void pop() {
stack.pop();
}
// 返回栈顶元素
public int peek() {
return stack.peek();
}
public boolean isEmpty() {
return stack.isEmpty();
}
}
单调栈应用场景:下一个更大元素,即输入一个数组,返回一个等长的数组,对应索引位置存储着下一个更大的元素值,如果下一个更大元素不存在,则存储-1
该问题就可以直接利用上述单调栈的数据结构:
public int[] nextGreatElement(int[] nums) {
int[] ans = new int[nums.length];
int idx = nums.length - 1;
for (int i = nums.length - 1; i >= 0; i--) {
ans[idx--] = monotonicStack.isEmpty() ? -1 : monotonicStack.peek();
monotonicStack.push(nums[i]);
}
return ans;
}
单调队列(Monotonic Queue)
单调队列和单调栈是非常相似的,只是一个本质上是队列,一个本质上是栈,扩增的需求都是需要保证队列内或栈内元素的单调性。
单调队列功能需求:
-
push()
方法:队尾添加元素,但是在添加时需要删除队列中小于/大于当前元素的其他元素,以保证队列中数据呈现单调增大/单调减小 -
pop()
方法:队头删除元素 -
max()
方法:返回队列中的最大值 -
min()
方法:返回队列中的最小值
public class MonotonicQueue {
// 用双向链表维护一个队列
private LinkedList<Integer> queue = new LinkedList<>();
// 保证队列是单调递减的
public void push(int num) {
while (!queue.isEmpty() && queue.getLast() < num) {
queue.pollLast();
}
queue.addLast(num);
}
// 弹出指定队头元素
public void pop(int num) {
if (num == queue.getFirst()) {
queue.pollFirst();
}
}
// 返回队头元素, 即队列中的最大值
public int max() {
return queue.getFirst();
}
// 返回队尾元素, 即队列中的最小值
public int min() {
return queue.getLast();
}
单调队列应用场景:滑动窗口最大值,即输入一个数组和滑动窗口的大小,该滑动窗口在数组上从左至右滑动,输出滑动过程中窗口内元素的最大值。
public int[] maxSlidingWindow(int[] nums, int k) {
int[] ans = new int[nums.length - k + 1];
int idx = 0;
for (int i = 0; i < k; i++) {
if (i < k - 1) {
monotonicQueue.push(nums[i]);
}
else {
monotonicQueue.push(nums[i]);
ans[idx++] = monotonicQueue.max();
monotonicQueue.pop(nums[i - k + 1]);
}
}
return ans;
}