239. 滑动窗口最大值【单调队列】

239. 滑动窗口最大值

在这里插入图片描述


C代码:单调队列

int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize){
    int q[numsSize];
    int l = 0, r = 0;             
    for (int i = 0; i < k; ++i) { 
        while (l < r && nums[i] >= nums[q[r-1]]) {  // >= 很重要
            --r;
        }
        q[r++] = i;     // 数组构造单调队列,头最大;
    }                   // 队列q[]存放下标i, why? 因为队列头虽然最大,但是有不在滑动窗口的数据,
                        // 如何区分数据在不在滑动窗口中,存放下标!
    int* ans = malloc(sizeof(int) * (numsSize - k +1));
    int j = 0;
    ans[j++] = nums[q[l]];
    for (int i = k; i < numsSize; ++i) {
        while (l < r && nums[i] >= nums[q[r-1]]) {  
            --r;
        }                        // >= 很重要, 滑动窗口为一段的最大值,若不放入进去,就会缺少值
        q[r++] = i;
        while (q[l] <= i - k) {  // q[]数组里面的值并没有减少,操作指针实现!
            ++l;                 
        }
        ans[j++] = nums[q[l]];   // 记录最大值之前要确保 队列最大值在滑动窗口之内
    }
    *returnSize = j;
    return ans;
}

Java代码1:优先级队列、O(nlogn)

初始时,我们将数组 nums 的前 k 个元素放入优先队列中。

每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。

然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。

因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。

这个方法在宏观上 更容易理解,相当于封装了;
代码2,就在处理排序的细节了,相当于在代码1的基础上优化,思路还是不错的。

public int[] maxSlidingWindow(int[] nums, int k) {
    int n = nums.length;
    // PriorityQueue<> pq = new Priority<>(); 就是一个容器;
    // int[]是容器存放的内容,始终可以根据条件对数组排序。
    PriorityQueue<int[]> pq = new PriorityQueue<>(new Comparator<int[]>() {
        @Override
        public int compare(int[] pair1, int[] pair2) {
            // 不相等 ? 按值降序 :按下标降序
            return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
        }
    });
    // offer poll peek
    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});
        // 移除不在滑动窗口的最大值,此处 k 为滑动窗口长度
        while (pq.peek()[1] <= i - k) {
            pq.poll();
        }
        ans[i - k + 1] = pq.peek()[0];
    }
    return ans;
}

Java代码2:单调队列、O(n)

在优先级队列的思路上进行优化。

public int[] maxSlidingWindow(int[] nums, int k) {
    int n = nums.length;
    Deque<Integer> deque = new LinkedList<>();

    for (int i = 0; i < k; ++i) {
        while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
            deque.pollLast();  // 队列开头永远是最大值:5 -3、5 3 7(不存在,while会把5 3干掉)
        }
        deque.offerLast(i);  // 放入下标
    }

    int[] ans = new int[n - k + 1];
    ans[0] = nums[deque.peekFirst()];  // 上面确保deque的第一值最大

    for (int i = k; i < n; ++i) {
        while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
            deque.pollLast();
        }
        deque.offerLast(i);

        while (deque.peekFirst() <= i - k) {  //上面 nums[i] 大于 队列末尾值,才替代;要是小于的话就不;
            deque.pollFirst();
        }
        ans[i - k + 1] = nums[deque.peekFirst()];
    }
    return ans;
}

Java代码3:暴力,超时,O(n * k)

暴力的解题思路比较直观!可借鉴。
但是,暴力在遍历过程中有重复的部分。

public int[] maxSlidingWindow(int[] nums, int k) {
    int n = nums.length -k + 1;
    int[] arr = new int[n];
    for (int i = 0; i < n; ++i) {
        int num = nums[i];
        for (int j = i; j < i + k; ++j) {
            num = Math.max(num, nums[j]);
        }
        arr[i] = num;
    }
    return arr;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值