Leetcode-滑动窗口

315 阅读5分钟

滑动窗口

滑动窗口(Sliding Window)是一种常见的算法技巧,用于处理数组、字符串或序列等数据结构的子序列问题。它通过维护一个固定大小的窗口来追踪特定条件下的子序列,以解决各种问题。以下是关于滑动窗口问题的一些总结和归纳:

滑动窗口的基本思想:

  1. 定义两个指针,通常为窗口的左边界(left)和右边界(right)。
  2. 通过移动指针来调整窗口的大小,以满足问题的特定要求。
  3. 在每个步骤中,更新窗口内的状态,计算所需的结果,并在需要时更新最终答案。

滑动窗口问题的步骤:

  1. 初始化指针和其他变量,定义窗口大小。
  2. 开始遍历数组或序列,根据问题要求移动窗口的边界。
  3. 在窗口内计算所需的结果,更新最终答案。
  4. 根据问题要求,调整窗口的大小和边界。
  5. 重复步骤2-4,直到遍历完整个数组或序列。

常见问题类型:

  1. 最大/最小值问题:找到滑动窗口内的最大或最小值。
  2. 子数组和子串问题:找到满足特定条件的子数组或子串。
  3. 字符串匹配问题:在字符串中找到满足特定条件的子串或字符。
  4. 计数问题:统计滑动窗口内满足条件的元素个数。
  5. 移动问题:通过移动窗口解决问题,如平均值、中位数等。

滑动窗口的优点:

  1. 时间复杂度通常为 O(N),其中 N 是数组或序列的长度,因此在大多数情况下,滑动窗口是一个高效的解决方案。
  2. 在处理连续子序列问题时,滑动窗口通常比传统的迭代方法更快,因为它避免了重复计算。

滑动窗口的注意事项:

  1. 窗口大小通常是固定的,但在某些问题中可能会根据条件变化。
  2. 窗口内的元素可以是连续的,也可以是不连续的,具体取决于问题的性质。
  3. 在移动窗口边界时,要注意边界条件,以避免数组越界。
  4. 对于某些问题,可能需要使用额外的数据结构(如哈希表)来辅助计算。

滑动窗口相关的题目

题目描述:给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。 请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。 任何误差小于 10-5 的答案都将被视为正确答案。

/**
 * 在数组中求连续n个元素的最大平均数(最大和)
 *  1 求数组前n-1个元素的和
 *  2 从n开始遍历数组,
 *      加上当前的元素并且减去尾部的元素即为当前和
 *      与之前的最大和比较,取最大值
 *      最大和除以n即为最大平均数
 *
 * @param nums 输入数组
 * @param k k个元素
 * @return 长度为n的最大平均数
 */
public double findMaxAverage(int[] nums, int k) {
    int tmp = 0;
    for (int i = 0; i < k; i++) {
        tmp+=nums[i];
    }
    int max = tmp;
    for (int i = k; i < nums.length; i++) {
        tmp = tmp + nums[i] - nums[i-k];
        max = Math.max(max,tmp);
    }
    return max / (k * 1.0);
}

题目描述:给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k0 ,则返回 数组中连续 1 的最大个数

/**
 * 反转n个值为0的元素,返回数组中连续1的最大个数:
 *   遍历数组:
 *      使用右指针找到第n+1个0的位置
 *      滑动窗口,移动左指针:
 *          数组尾部的值为0,则0的数量要减1,右移左指针
 *          数组尾部的值为1,则0的数量不变,右移左指针
 *      此时,左右指针之间的距离即为本次的值为1的长度,与之前的最大长度作比较,取较大的那个
 *
 * @param nums 输入数组
 * @param k 翻转为1的元素数量
 * @return 连续为1的最大个数
 */
public int longestOnes(int[] nums, int k) {
    int leftIndex = 0;
    int maxLen = 0;
    int zeroCount = 0;
    for(int i = 0; i < nums.length; ++i){
        if(nums[i] == 0){
            zeroCount++;
        }
        while(zeroCount > k){
            if(nums[leftIndex] ==0){
                zeroCount--;
            }
            leftIndex++;
        }
        maxLen = Math.max(maxLen,i-leftIndex + 1);
    }
    return maxLen;

}

题目描述:给你一个二进制数组 nums ,你需要从中删掉一个元素。 请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。 如果不存在这样的子数组,请返回 0 。

/**
 * 删除1个元素后,输出数组中连续为1的最大长度:
 *     1 初始化两个指针,left 和 right,表示子数组的左右边界,以及一个变量 zero_count 记录当前子数组内0的个数。
 *     2 遍历数组,对于每个元素执行以下步骤:
 *          如果当前元素为0,增加 zero_count 计数。
 *          如果 zero_count 大于1(即子数组内有超过一个0),移动 left 指针,直到 zero_count 变为1,以保持子数组内最多只有一个0。
 *          计算当前子数组的长度(即 right - left),并与之前的最大长度比较,更新最大长度。
 *          将 right 指针向右移动,继续遍历下一个元素。
 *     3 最终返回最大长度。
 * @param nums 输入数组
 * @return 输出仅包含1的数组的最大长度
 */

public int longestSubarray(int[] nums) {
    int max = 0;
    int left=0;
    int zero_count = 0;
    for(int i = 0; i < nums.length; ++i){
        if(nums[i] == 0){
            zero_count++;
        }
        while(zero_count > 1){
            if(nums[left] == 0){
                zero_count--;
            }
            left++;
        }
        max= Math.max(max, i - left );
    }
    return max;

}

小结:

滑动窗口的时间复杂度通常为 O(N),其中 N 是数组或序列的长度,因此在大多数情况下,滑动窗口是一个高效的解决方案。在处理连续子序列问题时,滑动窗口通常比传统的迭代方法更快,因为它避免了重复计算。 总之,滑动窗口是一个强大的工具,适用于解决各种数组、字符串和序列相关的问题。通过理解其基本思想和应用方法,你可以更有效地解决各种编程竞赛、算法竞赛和实际应用中的问题。