算法思想之滑动窗口(一)

欢迎拜访雾里看山-CSDN博客
本篇主题 :算法思想之滑动窗口(一)
发布时间 :2025.4.6
隶属专栏算法

目录

滑动窗口算法介绍

滑动窗口是一种用于处理数组/字符串子区间问题的高效算法,通过维护动态窗口减少重复计算,将时间复杂度从暴力法的 O(n²) 优化至 O(n)。

核心思想

  • 窗口定义 :用两个指针(left right)表示当前处理的区间 [left, right]
  • 窗口移动:根据问题规则,右指针扩展窗口,左指针收缩窗口,动态调整窗口大小。
  • 核心优势:避免重复遍历元素,每个元素最多被访问两次。

时间复杂度

O(n),左右指针各遍历一次数组。

适用场景

  • 子数组/子串的极值问题(最大、最小)。
  • 需要统计满足条件的连续元素序列。
  • 窗口内元素满足单调性或有明确的收缩规则。

注意事项

  • 边界处理:空输入、窗口大于数组长度等情况。
  • 数据结构选择:根据问题使用哈希表、数组等记录窗口状态。
  • 负数处理:如求最大子数组和时,可能需要动态调整窗口起始位置(此时需结合前缀和或 Kadane 算法)。

例题

长度最小的子数组

题目链接

209. 长度最小的子数组

题目描述

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0

示例 1

输入target = 7, nums = [2,3,1,2,4,3]
输出2
解释 :子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2

输入target = 4, nums = [1,4,4]
输出1

示例 3

输入target = 11, nums = [1,1,1,1,1,1,1,1]
输出0

提示

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 104

算法思路

由于此问题分析的对象是一段连续的区间 ,因此可以考虑滑动窗口 的思想来解决这道题。

让滑动窗口满足:从 i 位置开始,窗口内所有元素的和小于 target (那么当窗口内元素之和第一次大于等于目标值的时候,就是i 位置开始,满足条件的最小长度)。

做法:将右端元素划入窗口中,统计出此时窗口内元素的和:

  • 如果窗口内元素之和大于等于 target :更新结果,并且将左端元素划出去的同时继续判断是否满足条件并更新结果(因为左端元素可能很小,划出去之后依旧满足条件)
  • 如果窗口内元素之和不满足条件: right++ ,另下⼀个元素进⼊窗口。

代码实现

cpp 复制代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size(), sum = 0, len = INT_MAX;
        for(int left = 0, right = 0; right < n; right++)
        {
            sum += nums[right];// 进窗口
            while(sum >= target)// 判断
            {
                len = min(len, right - left + 1);// 更新结果
                sum -= nums[left++];// 出窗口
            }
        }
        return len == INT_MAX ? 0 : len;
    }
};

无重复字符的最长子串

题目链接

3. 无重复字符的最长子串

题目描述

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:

输入 : s = "abcabcbb"
输出 :3
解释 : 因为无重复字符的最长子串是 "abc",所以其长度为 3

示例 2:

输入 : s = "bbbbb"
输出 : 1
解释 : 因为无重复字符的最长子串是 "b",所以其长度为 1

示例 3:

输入 : s = "pwwkew"
输出 : 3
解释 : 因为无重复字符的最长子串是 "wke",所以其长度为 3

请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

算法思路

在往后寻找无重复子串能到达的位置时,可以利用哈希表统计出字符出现的频次,来判断什么时候子串出现了重复元素。

研究的对象依旧是一段连续的区间,因此继续使用滑动窗口思想来优化。

让滑动窗口满足:窗口内所有元素都是不重复的。

做法:右端元素 ch 进入窗口的时候,哈希表统计这个字符的频次:

  • 如果这个字符出现的频次超过 1 ,说明窗口内有重复元素,那么就从左侧开始划出窗口,直到 ch 这个元素的频次变为 1 ,然后再更新结果。
  • 如果没有超过 1 ,说明当前窗口没有重复元素,可以直接更新结果。

代码实现

cpp 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[128] = {0};
        int n = s.size(),len = 0;
        for(int left  = 0, right = 0; right < n; right++)
        {
            hash[s[right]]++;// 进窗口
            while(hash[s[right]] > 1)// 判断
            {
                hash[s[left++]]--;// 出窗口
            }
            len = max(right - left + 1, len);// 更新结果
        }
        return len;
    }
};

三数之和最大连续1的个数 III

题目链接

1004. 最大连续1的个数 III

题目描述

给定一个二进制数组 nums 和一个整数 k,假设最多可以翻转 k 0 ,则返回执行操作后 数组中连续 1 的最大个数 。

示例 1

输入nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出6
解释 :[1,1,1,0,0,1 ,1,1,1,1,1 ]

粗体数字从 0 翻转到 1,最长的子数组长度为 6

示例 2

输入nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出10
解释 :[0,0,1,1,1 ,1 ,1,1,1,1 ,1,1,0,0,0,1,1,1,1]

粗体数字从 0 翻转到 1,最长的子数组长度为 10

提示

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1
  • 0 <= k <= nums.length

算法思路

不要去想怎么翻转,不要把问题想的很复杂,这道题的结果无非就是一段连续的 1 中间塞了 k0 嘛。

因此,我们可以把问题转化成:求数组中一段最长的连续区间,要求这段区间内 0 的个数不超过 k 个。

既然是连续区间,可以考虑使用滑动窗口来解决问题。

代码实现

cpp 复制代码
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n = nums.size(), sum = 0, len = 0;
        for(int left = 0, right = 0; right < n; right++)
        {
            if(nums[right] == 0)//进窗口
                sum++;
            while(sum > k)//判断
            {
                if(nums[left++] == 0)// 出窗口
                    sum--;
            }
            len = max(len, right - left + 1);// 更新结果
        } 
        return len;
    }
};

将 x 减到 0 的最小操作数

题目链接

1658. 将 x 减到 0 的最小操作数

题目描述

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1

示例 1:

输入nums = [1,1,4,2,3], x = 5
输出2
解释 :最佳解决方案是移除后两个元素,将x减到 0

示例 2

输入nums = [5,6,7,8,9], x = 4
输出-1

示例 3

输入nums = [3,2,20,1,1,3], x = 10
输出5
解释 :最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0

提示

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 104
  • 1 <= x <= 109

算法思路

题目要求的是数组左端+右端 两段连续的、和为x的最短数组,信息量稍微多一些,不易理清思路;我们可以转化成求数组内一段连续的、和为sum(nums) - x 的最长数组。此时,就是熟悉的滑动窗口问题了。

代码实现

cpp 复制代码
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int sum = 0;
        for(auto &num : nums)
        {
            sum+=num;
        }
        int target = sum - x;
        if(target < 0)
            return -1;
        int n = nums.size(), len = -1,temp = 0;
        for(int left = 0, right = 0; right < n; right++)
        {
            temp += nums[right];// 进窗口
            while(temp > target)// 判断
            {
                temp -= nums[left++];// 出窗口
            }
            if(temp == target)// 更新结果
                len = max(len, right - left + 1);
        }
        return len == -1 ? -1 : n - len;
    }
};

⚠️ 写在最后:以上内容是我在学习以后得一些总结和概括,如有错误或者需要补充的地方欢迎各位大佬评论或者私信我交流!!!

相关推荐
小森77671 小时前
(三)机器学习---线性回归及其Python实现
人工智能·python·算法·机器学习·回归·线性回归
振鹏Dong1 小时前
超大规模数据场景(思路)——面试高频算法题目
算法·面试
uhakadotcom1 小时前
Python 与 ClickHouse Connect 集成:基础知识和实践
算法·面试·github
uhakadotcom1 小时前
Python 量化计算入门:基础库和实用案例
后端·算法·面试
uhakadotcom2 小时前
使用 Python 与 BigQuery 进行交互:基础知识与实践
算法·面试
uhakadotcom2 小时前
使用 Hadoop MapReduce 和 Bigtable 进行单词统计
算法·面试·github
XYY3692 小时前
前缀和 一维差分和二维差分 差分&差分矩阵
数据结构·c++·算法·前缀和·差分
longlong int2 小时前
【每日算法】Day 16-1:跳表(Skip List)——Redis有序集合的核心实现原理(C++手写实现)
数据库·c++·redis·算法·缓存
24白菜头2 小时前
C和C++(list)的链表初步
c语言·数据结构·c++·笔记·算法·链表
刺客-Andy3 小时前
前端加密方式 AES对称加密 RSA非对称加密 以及 MD5哈希算法详解
前端·javascript·算法·哈希算法