你还在为查询滑动窗口最大值发愁吗?点开看最高效率解法!


本文已收录至 Github《小白学算法》系列:https://github.com/vipstone/algorithm

题目描述
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [3,3,5,5,6,7]
LeetCode:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/

题目解析

实现方法 1:暴力解法
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// 非空判断
if (nums == null || k <= 0) return new int[0];
// 最终结果数组
int[] res = new int[nums.length - k + 1];
for (int i = 0; i < res.length; i++) {
// 初始化最大值
int max = nums[i];
// 循环 k-1 次找最大值
for (int j = i + 1; j < (i + k); j++) {
max = (nums[j] > max) ? nums[j] : max;
}
res[i] = max;
}
return res;
}
}

实现方法 2:改良版
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// 非空判断
if (nums == null || k <= 0) return new int[0];
// 最终结果数组
int[] res = new int[nums.length - k + 1];
// 存储的数据为元素的下标
ArrayDeque<Integer> deque = new ArrayDeque();
for (int i = 0; i < nums.length; i++) {
// 1.移除左边超过滑动窗口的下标
if (i >= k && (i - k) >= deque.peek()) deque.removeFirst();
// 2.从最后面开始移除小于 nums[i] 的元素
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i])
deque.removeLast();
// 3.下标加入队列
deque.offer(i);
// 4.将最大值加入数组
int rindex = i - k + 1;
if (rindex >= 0) {
res[rindex] = nums[deque.peek()];
}
}
return res;
}
}

实现方法 3:优先队列
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// 非空判断
if (nums == null || k <= 0) return new int[]{};
// 最终结果数组
int[] res = new int[nums.length - k + 1];
// 优先队列
PriorityQueue<Integer> queue = new PriorityQueue(res.length, new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer i2) {
// 倒序排列(从大到小,默认是从小到大)
return i2 - i1;
}
});
// 第一轮元素添加
for (int i = 0; i < k; i++) {
queue.offer(nums[i]);
}
res[0] = queue.peek();
int last = nums[0]; // 每轮要移除的元素
for (int i = k; i < nums.length; i++) {
// 移除滑动窗口之外的元素
queue.remove(last);
// 添加新元素
queue.offer(nums[i]);
// 存入最大值
res[i - k + 1] = queue.peek();
// 记录每轮要移除的元素(滑动窗口最左边的元素)
last = nums[i - k + 1];
}
return res;
}
}
代码解读
PS:从上面的执行结果可以看出,使用优先队列的执行效率很低,这是因为每次插入和删除都需要重新维护最大堆的元素顺序,因此整个执行的效率就会很低。

实现方法 4:双端队列
移除最左边小于最大值的元素(保证滑动窗口的最大值在队首位置); 从队尾向前依次移除小于当前要加入到队列元素的值(淘汰小值且生命周期短的元素); 将新元素加入到队列末尾; 将最大值加入到最终结果的数组中。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// 非空判断
if (nums == null || k <= 0) return new int[]{};
// 最终结果数组
int[] res = new int[nums.length - k + 1];
// 优先队列
PriorityQueue<Integer> queue = new PriorityQueue(res.length, new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer i2) {
// 倒序排列(从大到小,默认是从小到大)
return i2 - i1;
}
});
// 第一轮元素添加
for (int i = 0; i < k; i++) {
queue.offer(nums[i]);
}
res[0] = queue.peek();
int last = nums[0]; // 每轮要移除的元素
for (int i = k; i < nums.length; i++) {
// 移除滑动窗口之外的元素
queue.remove(last);
// 添加新元素
queue.offer(nums[i]);
// 存入最大值
res[i - k + 1] = queue.peek();
// 记录每轮要移除的元素(滑动窗口最左边的元素)
last = nums[i - k + 1];
}
return res;
}
}
从上述结果可以看出,双端队列相比于优先级队列来说,因为无需重新计算并维护元素的位置,所以执行效率还是挺高的。

总结
福 利
CSDN旗下公众号全新搜索技能上线啦!
只要在公众号内回复消息
就能自动回复想搜索的内容啦!
现在体验有惊喜,每日参与搜索打卡,
连续打卡满3天、7天、14天
均有CSDN精美礼品相送 百分百有礼!快戳
每日体验CSDN公众号搜索功能打卡
更多精彩推荐
☞倪光南、求伯君“出山”:爱解 Bug、无惧“35岁魔咒”、编码之路痛并快乐!
☞饿了么技术往事点分享 点点赞 点在看
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

随时掌握互联网精彩
赞助链接
排名
热点
搜索指数
- 1 年轻干部一定要挺起脊梁冲锋在前 7913908
- 2 如台独突破红线我们将采取断然措施 7964630
- 3 普京同意停火提议 泽连斯基回应 7870680
- 4 你的民生“幸福账本” 请查收 7742441
- 5 海底捞小便门黄牛要抽20%补偿 7648336
- 6 今天数学浓度太高了 7511098
- 7 男子造谣顶流明星澳门输10亿被拘 7448764
- 8 真假鞋混卖 上海团伙获利超3000万 7339112
- 9 芯片界三巨头掌门人皆为华人 7262719
- 10 张宁手真硬 7124807