算法-leetcode-滑动窗口问题- 239. 滑动窗口最大值

10, 239. 滑动窗口最大值

思路1: 暴力解法: 就直接从k-1处遍历,然后找到最大值,放进数组。

思路2:通过大顶堆(优先队列)实现

思路3:单调队列

思路4:直接通过数组和算法来实现:

package com.shangguigu.dachang.algrithm.A04_SlidingWindow;

import java.util.*;

/**
 * @author : 不二
 * @date : 2022/4/8-下午9:42
 * @desc : 239. 滑动窗口最大值
 * https://leetcode-cn.com/problems/sliding-window-maximum/
 **/
public class A36_maxSlidingWindow {
    public static void main(String[] args) {

         int[] nums = {1,3,-1,-3,5,3,6,7};
//        int[] nums = {7, 2, 4};
        int k = 3;
//        int[] ints = maxSlidingWindow_by_violence(nums, 3);
//        int[] ints = maxSlidingWindow_by_maxHeap(nums, 3);
//        int[] ints = maxSlidingWindow_by_dequeue(nums, 2);
        int[] ints = maxSlidingWindow_by_array(nums, 3);
        // [3,3,5,5,6,7]
        System.out.println("结果是:" + Arrays.toString(ints));

    }


    /**
     * 思路4:直接通过数组和算法来实现:
     *       这里时间复杂度O(n)
     *       分块 + 预处理
     *       首先定义一个数组1记录:原数组从左到右:从正好切分的窗口到当前位置的最大值。
     *       然后定义一个数组2记录:愿数组从右到左:到当前位置的最大值
     *                     start     end
     *       比如:原数组: 1, 3, -1,   -3, 5, 3,   6, 7
     *
     *    从左往右算数组1   1, 3,  3,  [-3], 5, 5,  6, 7
     *
     *    从右往左算数组2   3,[3],-1,    5, 5, 3,   7, 7
     *
     *    然后:如果计算的刚好是切分的窗口,那么数组1最右 和 数组2最左相等,均为最大值
     *         如果计算的跨窗口, 比如start为3, end为-3, 那么 数组1[end], 数组2[start] 哪个大就选择哪个即可
     *
     *                         start     end
     *       比如:原数组: 1, 3, -1,   -3, 5, 3,   6, 7
     *
     *    从左往右算数组1   1, 3,  3,   -3,[5], 5,  6, 7
     *
     *    从右往左算数组2   3, 3, [-1],  5, 5, 3,   7, 7
     *
     * @param nums
     * @param k
     * @return
     */
    public static int[] maxSlidingWindow_by_array(int[] nums, int k) {
        int[] result = new int[nums.length - k + 1];
        int[] leftMax = new int[nums.length];
        int[] rightMax = new int[nums.length];

        int max = Integer.MIN_VALUE;

        for (int i = 0; i < nums.length; i++) {
            // 这么处理也可以
            /*if (i % k == 0) {
                max = Integer.MIN_VALUE;
            }
            max = Math.max(max, nums[i]);
            leftMax[i] = max;*/

            // 每个窗口从左往右 到当前最大值
            if (i % k == 0) {
                leftMax[i] = nums[i];
            } else {
                leftMax[i] = Math.max(leftMax[i - 1], nums[i]);
            }

            int j = nums.length - 1 - i;
            // 这个地方判断还挺妙的
            if (j % k == k - 1 || j == nums.length - 1) {
                rightMax[j] = nums[j];
            } else {
                rightMax[j] = Math.max(rightMax[j + 1], nums[j]);
            }
        }
        System.out.println(Arrays.toString(leftMax));
        System.out.println(Arrays.toString(rightMax));

        for (int i = k-1; i < nums.length; i++) {
            int start = i - (k - 1);
            int end = i;
            result[i - (k - 1)] = Math.max(leftMax[end], rightMax[start]);
        }

        return result;
    }

    /**
     * 思路3:思路2的话,虽然使用大顶堆实现了,但是顶堆内部依然需要排序啥的
     *       这里使用双向队列进行处理,就可以在线性复杂度内实现条件有序
     *       存储的时候,存储数据位置,方便拿出首位判断是否是对应窗口数据
     *       这个太牛了
     *      时间复杂度:O(n)
     *      双端队列(单调队列)又叫做Dequeue
     * @param nums
     * @param k
     * @return
     */
    public static int[] maxSlidingWindow_by_dequeue(int[] nums, int k){
        int[] result = new int[nums.length - k + 1];
        // 这里是定义双端队列
        ArrayDeque<Integer> deque = new ArrayDeque<>();

        for (int i = 0; i < nums.length; i++) {
            // 在对尾添加对应数据
            // deque.addLast(i);
            // 然后把之前的小于i的数据给删除掉,保证双端队列中的数据是倒序存在的
            // 注意:比较的是真实值,不要用索引进行比较了
            while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) {
                deque.removeLast();
            }

            // 走完前面这一步,双端队列中的数据必然都大于当前值, 并把当前值(索引位置)加入队列尾部
            deque.addLast(i);

            if (i + 1 >= k) {
                // 则说明窗口达到指定宽度,可以计算结果了
                // 窗口的起始位置是:i+1-k, 结束位置是:i
                while (deque.getFirst() < i + 1 - k) {
                    deque.removeFirst();
                }
                // 移除不在当前窗口范围的最大值, 直到双端队列的首个索引处于需要的窗口内
                result[i + 1 - k] = nums[deque.getFirst()];
            }
        }
        return result;
    }

    /**
     * 思路2:通过大顶堆(优先队列)实现
     * 时间复杂度:O(nlogk)
     * @param nums
     * @param k
     * @return
     */
    public static int[] maxSlidingWindow_by_maxHeap(int[] nums, int k) {
        int[] result = new int[nums.length - k + 1];

        // 通过优先队列实现大顶堆(默认是小顶堆)
        PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(k, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                // 后减去前代表是倒序,前减去后代表是正序
                return o2-o1;
            }
        });

        /*for (int i = k-1; i < nums.length; i++) {
        }*/
        for (int i = 0; i < nums.length; i++) {
            maxHeap.add(nums[i]);
            System.out.println("i:" + i + "---" + nums[i]);

            // 说明已经等于
            if (i+1 >= k) {
                result[i + 1 - k] = maxHeap.peek();
                maxHeap.remove(nums[i + 1 - k]);
            }
            System.out.println(Arrays.toString(result));
        }
        return result;
    }


    /**
     * 思路1: 暴力解法: 就直接从k-1处遍历,然后找到最大值,放进数组。
     * @param nums
     * @param k
     * @return
     */
    public static int[] maxSlidingWindow_by_violence(int[] nums, int k) {

        int[] result = new int[nums.length - k + 1];
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i = k-1; i < nums.length; i++) {
            int max = nums[i];

            for (int j = i; j >= i - k + 1; j--) {
                max = Math.max(max, nums[j]);
            }
            result[i - k + 1] = max;
        }
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值