君子之交淡如水|

庄周de蝴蝶

园龄:6年9个月粉丝:0关注:0

单调栈、单调队列及其应用

单调栈定义

单调栈,顾名思义,即内部元素满足单调递增(递减)的栈,下面用Java代码展示一个栈内单调递减的实现:

public static void main(String[] args) {
int[] nums = { 3, 7, 6, 5, 4, 1, 8, 2 };
Deque<Integer> stack = new LinkedList<>();
for (int num : nums) {
// 如果栈非空,判断栈顶元素是否小于当前遍历到的 num
// 如果栈顶元素小于 num, 说明 num 入栈后就无法满足单调递减
// 循环遍历弹出所有小于 num 的元素,以便满足 num 入栈后,栈内元素依旧单调递减
while (!stack.isEmpty() && stack.peekLast() < num) {
stack.pollLast();
}
// 到这里,栈内要么为空, 要么都是大于 num 的元素
// num 入栈,依旧满足单调递减
stack.addLast(num);
System.out.println(stack);
}
}
// 运行结果如下:
[3] - 栈空,3入栈
[7] - 栈顶为3,小于7,不满足递减,3出栈,栈空循环结束,7入栈
[7, 6] - 栈顶为7,大于66入栈
[7, 6, 5] - 栈顶为6,大于55入栈
[7, 6, 5, 4] - 栈顶为5,大于44入栈
[7, 6, 5, 4, 1] - 栈顶为4,大于11入栈
[8] - 栈内元素均小于8,挨个判断并出栈,直到栈空循环结束,然后8入栈
[8, 2] - 栈顶为8,大于22入栈

单调栈应用

下面来展示具体应用:

下一个更大元素 Ⅰ

image-20210307130838481

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ne… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

由于题目说明数组中不存在重复元素,而且nums1nums2的子集,所以我们可以先不管nums1,使用一个Map保存nums2中每一个元素的下一个更大的数字即可,下面展示具体实现:

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
// 根据 nums2 获取保存 nums2 中每一个元素的<下一个更大的数字>的 Map
Map<Integer, Integer> map = getMap(nums2);
for (int i = 0; i < nums1.length; i++) {
// 如果 Map 中存在指定数字的 key, 说明该数字存在<下一个更大的数字>
// 否则说明该数字仍然保存在栈中未弹出, 即不存在<下一个更大的数字>, 返回-1
nums1[i] = map.getOrDefault(nums1[i], -1);
}
return nums1;
}
private Map<Integer, Integer> getMap(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
Deque<Integer> stack = new LinkedList<>();
for (int num : nums) {
// 如果栈非空,判断栈顶元素是否小于当前遍历的 num
// 如果满足则说明栈顶元素的<下一个更大的数字>为 num
// 弹出栈顶元素并保存在 Map 中, 开始下一个判断
while (!stack.isEmpty() && stack.peekLast() < num) {
map.put(stack.pollLast(), num);
}
// 到这里,栈内要么为空, 要么栈顶元素大于 num
// 即栈顶元素的<下一个更大的数字>不是 num, num 入栈
stack.addLast(num);
}
return map;
}

每日温度

image-20210307133101914

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/da… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

由于此题要求我们存储的是和下一个更大数字之间的距离,因此我们只要在栈内储存元素的下标即可,下面是实现:

public int[] dailyTemperatures(int[] T) {
int n = T.length;
int[] res = new int[n];
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < n; i++) {
// 如果栈非空,判断栈顶元素下标对应的温度是否小于遍历的当前 T[i] 温度
// 如果小于则说明当前温度 T[i] 比栈顶元素下标对应的温度高
// 弹出元素,记录该下标与当前温度 T[i] 之间的距离,然后判断下一个元素
while (!stack.isEmpty() && T[stack.peekLast()] < T[i]) {
res[stack.peekLast()] = i - stack.pollLast();
}
stack.addLast(i);
}
return res;
}

单调队列介绍及应用

单调队列中的其实并不是表面意义上的普通队列,而指的是双向队列,具体实现是当在添加元素时和单调栈一样都是在尾部进行操作,但是在取元素时则是从头部就行获取,下面就以一道经典的滑动窗口题展示其具体应用:

滑动窗口最大值

image-20210327103939242

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/sl… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

我们只需要维护一个单调递减的队列,这样队头就一直是最大值,队列内保存元素的下标,并且当队头元素存储的下标不在符合的范围内时,需要从队头出头:

public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
int pos = 0;
int resArrLen = n - k + 1;
int[] res = new int[resArrLen];
Deque<Integer> deque = new LinkedList<>();
for (int i = 0; i < n; i++) {
// 添加元素和单调栈是一个思路
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
deque.pollLast();
}
deque.addLast(i);
// 判断当前下标是否大于等于区间范围
// 满足了才开始添加值
if (i >= k - 1) {
// 获取队头存储的下标
Integer tmp = deque.peekFirst();
// 判断队头的下标是否在区间范围内
// 如果大于就从队头出队
if (i - tmp > k - 1) {
deque.pollFirst();
}
// 然后当前队头即为区间范围内的最大值
res[pos++] = nums[deque.peekFirst()];
}
}
return res;
}

总结

本文简单介绍了单调栈和单调队列的含义和简单应用,希望能够对你有所帮助。

本文作者:庄周de蝴蝶

本文链接:https://www.cnblogs.com/butterfly-fish/p/17762291.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   庄周de蝴蝶  阅读(7)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起