使用滑动窗口解决问题

102 阅读2分钟

前言

算法是我们面试中经常被问到也是比较难的一部分,解决这类问题可以使用暴力破解(有时候可以有时候由于用例难度比较高可能会超时)或者使用技巧

今天要讲解的是如何使用滑动窗口的去解决部分算法问题

什么样的问题适合用滑动窗口

1. 在数组或字符串中获取定长的子数组或者子字符串是否满足某一条件

  1. 大小为 K 且平均值大于等于阈值的子数组数目
    给你一个整数数组arr和两个整数k和threshold
    请你返回长度为k且均值大于等于threshold的子数组数目
此题由于限定了子数组的长度,所以如果我们使用滑动窗口处理是十分简单的
思路:
1. 首先从下标为0开始获取定长K的子数组
2. 如果当前获取到的子数组满足条件,那么满足条件的子数组数目+1
3. 从下标为k的下标开始进行遍历,将原有数组+1,再去除下标为i-k位置的值,判断是否满足条件
4. 依次类推知道遍历数组中的所有元素,统计满足条件的值返回数目
代码如下
public int numOfSubarrays(int[] arr, int k, int threshold) {
        int sum = 0;
        for (int i = 0; i < k; i++) {
            sum += arr[i];
        }

        int times = 0;
        if (sum >= threshold * k) {
            times++;
        }

        for (int i = k; i < arr.length; i++) {
            sum = sum + arr[i] - arr[i - k];
            if (sum >= threshold * k) {
                times++;
            }
        }
        return times;
    }

2. 未给定k值但是为连续子数组

  1. 绝对差不超过限制的最长连续子数组
    给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit,如果不存在满足条件的子数组,则返回 0 。
思路
1. 求绝对差,那么我们只需要使用一个有序集合存储我们遍历时的子数组,我们使用right从左往右开始向集合内添加元素
2. 判断子数组内部最大值和最小值的差是否小于等于limit
3. 如果不满足,那么需要移动left去除集合内的元素直到满足条件为止
4. 判断满足条件集合的长度,取最大值返回
public int longestSubarray(int[] nums, int limit) {
        int left = 0, right = 0, maxSize = 0;
        TreeMap<Integer, Integer> map = new TreeMap<>();

        while (right < nums.length) {
            map.put(nums[right], map.getOrDefault(nums[right], 0) + 1);

            while (map.lastKey() - map.firstKey() > limit) {
                map.put(nums[left], map.get(nums[left]) - 1);
                if (map.get(nums[left]) == 0) {
                    map.remove(nums[left]);
                }
                left++;
            }

            maxSize = Math.max(maxSize, right - left + 1);
            right++;
        }
        return maxSize;
    }

滑动窗口的关键

滑动窗口的关键在于维护着一个一定长度的窗口,我们移动窗口内的元素来使其达到我们解题所需要的值