0
点赞
收藏
分享

微信扫一扫

LeetCode_Queue_239. Sliding Window Maximum 滑动窗口最大值【优先队列,单调队列】【java】【中等】


目录

​​一,题目描述​​

​​英文描述​​

​​中文描述​​

​​示例与说明​​

​​二,解题思路​​

​​1,优先队列​​

​​2,单调队列​​

​​三,AC代码​​

​​Java​​

​​优先队列​​

​​单调对列​​

​​四,解题过程​​

​​第一搏​​

​​第二搏​​

​​第三搏​​

一,题目描述

英文描述

You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.

Return the max sliding window.

中文描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例与说明

LeetCode_Queue_239. Sliding Window Maximum 滑动窗口最大值【优先队列,单调队列】【java】【中等】_单调队列

LeetCode_Queue_239. Sliding Window Maximum 滑动窗口最大值【优先队列,单调队列】【java】【中等】_困难_02

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

二,解题思路

参考​​@力扣官方题解【滑动窗口最大值】​​

1,优先队列

最直观的解法就是采用优先队列。

队列中包括滑动区间内的所有值。每次取值时,判断队头是否属于滑动区间内的元素,若不是则弹出,否则直接返回即可。

为了方便的判断元素位置,可以考虑在优先队列中存放大小为2的数组(位置1:元素值,位置2:元素下标)。

2,单调队列

优先队列的作用主要是为了获得滑动窗口内的最大值,然而若只是想获得窗口内最大值的话,维持一个普通队列就可以了。

  • 当向队尾插入一个元素时,只需要将队列中小于等于该元素的值从后向前依次弹出即可;
  • 若队头元素不属于滑动区间范围内,则从前向后弹出即可;

这样队头的元素一定是最大,且最新的(位于滑动窗口右侧,相较于左侧淘汰的晚些);

可以从上面看出,队列需要支持队头和队尾操作,因此我们采用双端队列。

此外,为了方便的判断元素的位置,队列中存放的值应该为数组中元素对应的下标(有下标后就可以很方便的获得对应的值)。因此队列中存放的值(数组下标)总保持递增的顺序,也就是通常所说的单调队列。

三,AC代码

Java

优先队列

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// int[2]:位置0表示元素大小,位置1表示元素位置
PriorityQueue<int[]> heap = new PriorityQueue<>(new Comparator<int[]>(){
public int compare(int[] a, int[] b) {
// 元素大小不等时,按照大根堆的方法比较
// 元素大小相等时,位置越小,优先级越高,越先被弹出
return a[0] != b[0] ? b[0] - a[0] : a[1] - b[1];
}
});
int[] ans = new int[nums.length - k + 1];
for (int i = 0; i < k - 1; i++) {
heap.offer(new int[]{nums[i], i});
}
for (int i = k - 1; i < nums.length; i++) {
heap.offer(new int[]{nums[i], i});
while (heap.peek()[1] < i - k + 1) {
heap.poll();
}
ans[i - k + 1] = heap.peek()[0];
}
return ans;
}
}

单调对列

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> queue = new LinkedList<>();
// 将前k-1个数放入队列中
for (int i = 0; i < k - 1; i++) {
while (!queue.isEmpty() && nums[i] >= nums[queue.peekLast()]) {// !!!加上等于 !!!这里需要与队列末尾进行比较,而不是队头
queue.pollLast();
}
queue.offerLast(i);
}
int[] ans = new int[nums.length - k + 1];
// 从k-1开始遍历数组
for (int i = k - 1; i < nums.length; i++) {
// 若队尾元素小于当前元素,则弹出队尾
while (!queue.isEmpty() && nums[i] >= nums[queue.peekLast()]) {// !!!加上等于
queue.pollLast();
}
queue.offerLast(i);
// 将队头超过滑动窗口区间的元素弹出
while (queue.peekFirst() < i - k + 1) {
queue.pollFirst();
}
ans[i - k + 1] = nums[queue.peekFirst()];
}
return ans;
}
}

四,解题过程

第一搏

维护大小为k的大根堆,滑动窗口每移动一格,删除原窗口中第一个元素(heap.remove(nums[i])),将新窗口最后一个元素添加到堆中,返回堆顶即可。

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
PriorityQueue<Integer> heap = new PriorityQueue<>(new Comparator<Integer>(){
public int compare(Integer a, Integer b) {
return b - a;// 大根堆
}
});
int[] ans = new int[nums.length - k + 1];
for (int i = 0; i < k - 1; i++) {
heap.offer(nums[i]);
}
for (int i = 0; i + k <= nums.length; i++) {
heap.offer(nums[i + k - 1]);
ans[i] = heap.peek();
heap.remove(nums[i]);
}
return ans;
}
}

第二搏

显然,在堆中查找并删除元素是非常耗时的!

考虑要删除的元素是指滑动窗口第一个值,要么根据值来删除(同上,采用remove),要么根据位置来删除。

若堆顶元素的位置不在滑动区间范围内,则将其移除。

因此就需要额外存储元素的位置。

LeetCode_Queue_239. Sliding Window Maximum 滑动窗口最大值【优先队列,单调队列】【java】【中等】_单调队列_03

LeetCode_Queue_239. Sliding Window Maximum 滑动窗口最大值【优先队列,单调队列】【java】【中等】_leetcode_04

第三搏

优先队列的作用主要是为了获得滑动窗口内的最大值,然而若只是想获得窗口内最大值的话,维持一个普通队列就可以了。

  • 当向队尾插入一个元素时,只需要将队列中小于等于该元素的值从后向前依次弹出即可;
  • 若队头元素不属于滑动区间范围内,则从前向后弹出即可;

这样队头的元素一定是最大,且最新的(位于滑动窗口右侧,相较于左侧淘汰的晚些);

可以从上面看出,队列需要支持队头和队尾操作,因此我们采用双端队列。

此外,为了方便的判断元素的位置,队列中存放的值应该为数组中元素对应的下标(有下标后就可以很方便的获得对应的值)。因此队列中存放的值(数组下标)总保持递增的顺序,也就是通常所说的单调队列。

LeetCode_Queue_239. Sliding Window Maximum 滑动窗口最大值【优先队列,单调队列】【java】【中等】_困难_05

举报

相关推荐

0 条评论