算法---LeetCode 239. 滑动窗口最大值(剑指offer 59-1)

1. 题目

原题链接

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 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 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
注意:本题与主站 239 题相同:https://leetcode-cn.com/problems/sliding-window-maximum/
Related Topics 队列 Sliding Window
👍 268 👎 0

2. 题解

2.1 解法1: 优先队列(堆)

  1. 设置最大堆, 其中保存二元组 (nums[i],i)
  2. 将前k个元素加入堆, 然后移动右指针, 遍历 k 之后的元素
  3. 当 堆顶元素 的 index 小于等于 当前左指针值时, 说明当前堆顶元素不在窗口内, 出堆,
    否则 当前堆顶元素即为 当前窗口的最大值, 将其加入结果集即可
  4. 由于过程中使用了 k-1, 注意先排除 k=0的情况
    class Solution {
        public int[] maxSlidingWindow(int[] nums, int k) {
            int n = nums.length;
            int[] ans = new int[n - k + 1];
            PriorityQueue<int[]> maxHeap = new PriorityQueue<>(new Comparator<int[]>() {
                @Override
                public int compare(int[] o1, int[] o2) {
                    return o1[0] != o2[0] ? o2[0] - o1[0] : o2[1] - o1[1];
                }
            });
            // 将前k 个元素入堆
            for (int i = 0; i < k; i++) {
                maxHeap.offer(new int[]{nums[i], i});
            }
            // 将第一个元素堆顶加入结果集
            ans[0] = maxHeap.peek()[0];
            int t = 1;
            // 开始移动窗口, 此时左边界下标为 i-k+1
            for (int i = k; i < n; i++) {
                maxHeap.offer(new int[]{nums[i], i});
                while (maxHeap.peek()[1] < i - k + 1) {
                    maxHeap.poll();
                }
                ans[t++] = maxHeap.peek()[0];
            }
            return ans;
        }
    }

参考:官方题解

2.2 解法2: 单调递减双端队列

  1. 维护一个存储下标的元素值单调递减的双端队列, 其中队头为最大值, 队尾为最小值
  2. 然后移动有边界, 执行循环遍历
    (1) 先对队列进行入队前的递减维护, 当队列不空且队尾元素小于等于右边界, 队尾元素出队
    (2) 将右边界元素加入队尾
    (3) 如果队头元素下标小于左边界, 队头出队
    (4) 判断是否形成窗口, 若是将结果加入结果集中

队列的队头相当于保存了当前窗口的最大值, 每次先维护其单调递减性, 然后将元素加入队列, 接着判断队头是否在窗口内并做出队头操作, 保证队头最大且在窗口内, 最后取得队头最大值为当前窗口最大值

    class Solution {
        public int[] maxSlidingWindow(int[] nums, int k) {
            if (nums.length == 0 || k < 1) return new int[]{};
            Deque<Integer> deque = new LinkedList<>();
            int[] ans = new int[nums.length - k + 1];
            for (int i = 0; i < nums.length; i++) {
                while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                    deque.pollLast();
                }
                deque.offerLast(i);
                // 判断队首元素是否在左边界内, 不是就出队, 直到在左边界
                while (deque.peekFirst() < i - k + 1) {
                    deque.pollFirst();
                }
                if (i - k + 1 >= 0) {
                    ans[i - k + 1] = nums[deque.peekFirst()];
                }
            }
            return ans;
        }
    }

参考:双向队列解决滑动窗口最大值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\],可以使用暴力解法来求解滑动窗口的最大。具体的做法是,遍历数组,对于每个窗口,使用一个内部循环来找到窗口中的最大,并将其存储在结果数组中。时间复杂度为O(n*k),其中n为数组长度,k为窗口大小。 根据引用\[2\],还可以使用队列来求解滑动窗口的最大。具体的做法是,使用一个双端队列来维护一个单调递减的窗口。遍历数组,对于每个元素,首先判断队头是否在滑动窗口范围内,如果不在,则将其从队头移除。然后,将当前元素与队尾元素比较,如果当前元素大于队尾元素,则将队尾元素移除,直到队列为空或者当前元素小于等于队尾元素。最后,将当前元素的索引插入队尾。如果滑动窗口的元素个数达到了k个,并且始终维持在窗口中,就将队头元素加入答案数组中。时间复杂度为O(n),其中n为数组长度。 综上所述,可以使用暴力解法或者使用队列来求解leetcode滑动窗口的最大。 #### 引用[.reference_title] - *1* *3* [leetcode239. 滑动窗口最大](https://blog.csdn.net/kkkkuuga/article/details/124829581)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Leetcode#239. 滑动窗口最大 (Java解法)](https://blog.csdn.net/paranior/article/details/114890555)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值