青少年编程知识记录 codecoming

【算法】滑动窗口

一、定义

滑动窗口算法(Sliding Window Algorithm)是一种用于解决数组或字符串中子数组或子串问题的优化技术。它通过维护一个窗口(通常是数组或字符串的一个连续子区间),在遍历过程中动态调整窗口的起始和结束位置,从而高效地解决问题。

滑动窗口的核心思想是:

  • 窗口大小固定:窗口的大小在滑动过程中保持不变。

  • 窗口大小可变:窗口的大小根据条件动态调整。



二、使用场景

滑动窗口算法通常用于以下场景:

  1. 1.子数组/子串问题:

    • 求满足条件的最大或最小子数组/子串。

    • 例如:最大子数组和、最小覆盖子串、最长无重复字符子串等。

  2. 2.固定窗口大小问题:

    • 例如:计算大小为 k 的子数组的平均值。

  3. 3.优化暴力解法:

    • 将时间复杂度从 O(n²) 优化到 O(n)



三、示例



1.例题1:固定窗口大小

【题目描述】给定一个数组和一个整数 k,计算所有大小为 k 的子数组的平均值。

【题目分析】

(1) 什么是子数组?

  • 子数组是数组中连续的一段元素。

  • 例如,数组 [1, 3, 2, 6, -1] 的子数组包括 [1, 3, 2][3, 2, 6] 等。

(2)什么是大小为 k 的子数组?

  • 大小为 k 的子数组是指长度为 k 的连续子数组。

  • 例如,如果 k = 3,那么子数组的长度必须是 3。

(3)什么是子数组的平均值?

  • 子数组的平均值是指子数组中所有元素的和除以子数组的长度。

  • 例如,子数组 [1, 3, 2] 的平均值是 (1 + 3 + 2) / 3 = 2

(4)问题的目标

  • 对于数组中的每一个大小为 k 的子数组,计算其平均值,并返回所有平均值的集合。



【解题思路】

(1)暴力解法

  • 遍历数组,对于每一个起始位置 i,计算从 i 到 i + k - 1 的子数组的和,然后除以 k 得到平均值。

  • 时间复杂度:O(n * k),其中 n 是数组的长度。

(2)滑动窗口优化

  • 使用滑动窗口技术,避免重复计算子数组的和。

  • 初始化第一个窗口的和,然后在滑动窗口时,加上新元素,减去旧元素。

  • 时间复杂度:O(n)

#include <iostream>  using namespace std;  void sw(int nums[], int n, int k) {      if (n < k) return; // 如果数组长度小于k,直接返回      double windowSum = 0;      // 初始化窗口      for (int i = 0; i < k; i++) {          windowSum += nums[i];      }      cout << windowSum / k << " ";      // 滑动窗口      for (int i = k; i < n; i++) {          windowSum += nums[i] - nums[i - k]; // 加上新元素,减去旧元素          cout << windowSum / k << " ";      }      cout << endl;  }    int main() {      int nums[] = {1, 3, 2, 6, -1, 4, 1, 8, 2};      int n = sizeof(nums) / sizeof(nums[0]);      int k = 5;      sw(nums, n, k);      return 0;  }



【过程解释】

  1. 初始化窗口:

    • 计算第一个窗口的和 windowSum,即 nums[0] 到 nums[k-1] 的和。

    • 输出第一个窗口的平均值 windowSum / k

  2. 滑动窗口:

    • 从第 k 个元素开始,滑动窗口。

    • 每次滑动时,加上新元素 nums[i],减去旧元素 nums[i - k]

    • 输出当前窗口的平均值 windowSum / k

2.例题2:可变窗口大小



【题目描述】给定一个字符串,找到最长无重复字符的子串。

【题目分析】

(1)什么是子串?

  • 子串是字符串中连续的一段字符。

  • 例如,字符串 "abcabcbb" 的子串包括 "abc""bca""cbb" 等。

(2)什么是无重复字符的子串?

  • 无重复字符的子串是指子串中的所有字符都是唯一的,没有重复。

  • 例如,子串 "abc" 是无重复字符的,而子串 "abca" 包含重复字符 'a'

(3)什么是最长无重复字符的子串?

  • 在所有无重复字符的子串中,找到长度最长的那个。

  • 例如,字符串 "abcabcbb" 的最长无重复字符子串是 "abc",长度为 3。



【参考代码】

#include <iostream>  #include <cstring> // 用于 memset    using namespace std;    int ws(string s) {      int n = s.size();      int window[256]; // 用于存储字符的最近出现位置      memset(window, -1, sizeof(window)); // 初始化为 -1      int left = 0, right = 0; // 窗口的左右边界      int maxLen = 0;      while (right < n) {          char currentChar = s[right];          if (window[currentChar] != -1 && window[currentChar] >= left) {              // 如果字符在窗口中,收缩左边界              left = window[currentChar] + 1;          }          // 更新字符的最近出现位置          window[currentChar] = right;          // 更新最大长度          maxLen = max(maxLen, right - left + 1);          right++;      }      return maxLen;  }    int main() {     	string s;     	cin>>s;//abcabcbb  	cout<< ws(s) << endl;      return 0;  }



【解释】

  • (1)使用数组 window 存储字符的最近出现位置。

  • (2)如果字符在窗口中,收缩左边界;否则,扩展右边界。

  • (3)时间复杂度:O(n)



作者:亿万年的星光 分类:算法 浏览: