上篇:蜗牛爬窗!零基础吃透滑动窗口基础篇
一、趣味核心原理用生活场景读懂滑动窗口很多人觉得滑动窗口抽象其实它就是「动态移动的取景框」我们用蜗牛爬栏杆的场景秒懂核心想象一排栏杆数组/字符串一只蜗牛带着一个固定/可伸缩的窗户窗口在栏杆上爬行窗口栏杆上一段连续的区间[left, right]右指针right蜗牛的头负责向前探索不断纳入新元素左指针left蜗牛的尾负责收缩窗口剔除无效元素滑动核心头往前走尾巴按需动全程只遍历一次数组时间复杂度稳定 O(n)上篇主打固定窗口滑动窗户大小不变整体平移是滑动窗口的入门基础适合解决「定区间最值、定长度统计」类问题搭配4个由易到难的C实战案例零基础也能看懂。二、固定滑动窗口通用模板C// 固定窗口大小k int left 0; for(int right 0; right nums.size(); right){ // 1. 纳入right指向的新元素更新窗口状态 // 2. 当窗口长度达到k时开始维护窗口、更新答案 while(right - left 1 k){ // 剔除left指向的旧元素更新窗口状态 left; } // 3. 窗口合法统计结果 }三、上篇4个实战案例逐行代码详解案例1最长k个连续相同字符简单入门题目描述给定字符串求最多包含k个相同字符的最长子串长度固定窗口思想入门解题思路固定窗口动态统计字符数量窗口内相同字符数≤k则合法记录最大长度#includeiostream #includestring #includealgorithm using namespace std; int maxKCharSub(string s, int k) { int left 0, maxLen 0, maxCnt 0; int cnt[26] {0}; // 统计26个字母出现次数 // 右指针遍历整个字符串 for(int right 0; right s.size(); right){ int cur s[right] - a; cnt[cur]; // 纳入新字符 maxCnt max(maxCnt, cnt[cur]); // 更新窗口内最多字符数 // 窗口不合法总长度-最多字符数 k需要替换的字符超量 while(right - left 1 - maxCnt k){ cnt[s[left]-a]--; // 剔除左边界字符 left; // 左指针右移收缩窗口 } // 更新合法窗口最大长度 maxLen max(maxLen, right - left 1); } return maxLen; } int main(){ string s AABABBA; cout maxKCharSub(s, 1) endl; // 输出4 return 0; }代码详解定义cnt[26]数组统计窗口内字母频次maxCnt记录窗口内出现最多的字符数量右指针不断右移将新字符纳入窗口并更新频次核心判断窗口总长度-最多字符数是需要替换的字符数超过k则窗口无效左指针收缩每次窗口合法时更新最大子串长度最终得到结果。案例2滑动窗口最大值经典固定窗口题目描述给数组nums和窗口大小k窗口从左向右滑动输出每一个窗口内的最大值解题思路固定窗口大小k遍历数组每次窗口成型后统计最大值#includeiostream #includevector #includealgorithm using namespace std; vectorint maxSlidingWindow(vectorint nums, int k) { vectorint res; int left 0; for(int right 0; right nums.size(); right){ // 窗口长度达到k开始统计 if(right - left 1 k){ // 寻找当前窗口最大值 int maxVal nums[left]; for(int i left; i right; i){ maxVal max(maxVal, nums[i]); } res.push_back(maxVal); left; // 窗口整体右移 } } return res; } int main(){ vectorint nums {1,3,-1,-3,5,3,6,7}; vectorint ans maxSlidingWindow(nums, 3); for(int x : ans) cout x ; // 输出3 3 5 5 6 7 return 0; }代码详解右指针持续遍历数组当窗口长度刚好等于k时代表窗口成型遍历当前窗口所有元素找到最大值存入结果数组左指针右移1位实现窗口整体滑动继续遍历下一个窗口全程固定窗口大小简单直观适合新手理解固定窗口滑动逻辑。案例3定长子数组平均数简单实操题目描述给定数组计算所有长度为k的连续子数组的平均数解题思路固定窗口大小先算初始窗口和后续滑动时去头加尾避免重复计算提升效率#includeiostream #includevector using namespace std; vectordouble getKAverage(vectorint nums, int k) { vectordouble res; int sum 0; int left 0; for(int right 0; right nums.size(); right){ sum nums[right]; // 纳入新元素累加和 // 窗口成型 if(right - left 1 k){ res.push_back(sum * 1.0 / k); // 计算平均数 sum - nums[left]; // 剔除左边界元素 left; // 窗口滑动 } } return res; } int main(){ vectorint nums {1,2,3,4,5}; vectordouble ans getKAverage(nums, 2); for(double x : ans) cout x ; // 输出1.5 2.5 3.5 4.5 return 0; }代码详解用sum变量缓存窗口和避免每次遍历窗口求和优化时间复杂度右指针移动累加新元素值窗口成型后计算平均数窗口滑动核心减去左边界旧值右移左指针实现O(n)高效遍历通过浮点转换*1.0保证平均数为小数结果。案例4包含k个0的最长子数组基础进阶题目描述给定二进制数组仅0、1求最多包含k个0的最长子数组长度解题思路固定规则窗口统计窗口内0的数量超量则收缩左边界#includeiostream #includevector #includealgorithm using namespace std; int longestZeroSub(vectorint nums, int k) { int left 0, zeroCnt 0, maxLen 0; for(int right 0; right nums.size(); right){ // 遇到0则计数1 if(nums[right] 0) zeroCnt; // 0的数量超k窗口无效收缩左边界 while(zeroCnt k){ if(nums[left] 0) zeroCnt--; left; } // 更新最大长度 maxLen max(maxLen, right - left 1); } return maxLen; } int main(){ vectorint nums {1,1,0,0,1,1,0,1,1,0}; cout longestZeroSub(nums, 2) endl; // 输出7 return 0; }代码详解zeroCnt统计当前窗口内0的个数右指针遍历不断纳入新元素当0的数量超过k持续右移左指针剔除左侧元素直到窗口合法合法窗口下实时更新最大长度最终得到符合条件的最长子数组此案例是固定规则窗口的经典应用衔接下篇可变窗口知识点。上篇固定窗口例题分类思维导图谢谢