堆与滑动窗口问题的结合
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位,返回滑动窗口中的最大值。
输入: nums = [1,3,-1,-3,5,3,6,71,k = 3
输出: [3,3,5,5,6,7]
解释: 滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
这种方法在基础算法的堆部分出现过。对于最大值、K个最大这种场景优先队列 (堆)是首先应该考虑的思路。大根堆可以帮助我们实时维护一系列元素中的最大值。
本题初始时,我们将数组 nums 的前 k个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。
我们不断地移除堆顶的元素,直到其确实在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。文章来源:https://www.toymoban.com/news/detail-738627.html
为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组(num,index),表示元素 num 在数组中的下标为 index。文章来源地址https://www.toymoban.com/news/detail-738627.html
public class MaxSlidingWindow {
public static int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
// 定义最大堆(优先队列),比较的是num值,num值相等再比较索引
PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] pair1, int[] pair2) {
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
// 先入堆k个
for (int i = 0; i < k; ++i) {
pq.offer(new int[]{nums[i], i});
}
int[] ans = new int[n - k + 1]; // 存储滑动窗口中的最大值
ans[0] = pq.peek()[0];
for (int i = k; i < n; ++i) {
// 继续入堆
pq.offer(new int[]{nums[i], i});
// 如果最大值的索引不在滑动窗口内,则将其出堆,直到其在滑动窗口内
while (pq.peek()[1] <= i - k) {
pq.poll();
}
// 存储当前滑动窗口最大值
ans[i - k + 1] = pq.peek()[0];
}
return ans;
}
// 测试
public static void main(String[] args) {
int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
int k = 3;
int[] result = maxSlidingWindow(nums, k);
System.out.println(Arrays.toString(result));
}
}
到了这里,关于算法通关村 —— 滑动窗口与堆结合的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!