衡阳市网站建设_网站建设公司_Redis_seo优化
2026/1/1 15:39:48 网站建设 项目流程

双指针/滑动窗口—算法总结与教学指南 - 指南

核心思想总结

三种双指针模式

模式特点适用场景经典例题
同向双指针
(滑动窗口)
左右指针同向移动,维护一个窗口子数组/子串问题,满足某条件的最长/最短区间无重复字符的最长子串、长度最小的子数组
对向双指针左右指针从两端向中间移动有序数组查找、两数之和、回文判断两数之和 II、反转字符串
快慢指针快慢指针以不同速度移动链表环检测、中点查找、重复元素环形链表、寻找链表中点

滑动窗口解题框架

通用模板

int slidingWindow(vector<int>& nums, int k) {int left = 0;               // 左指针int result = 0;             // 结果unordered_map<int, int> freq; // 频率统计(根据需要)for (int right = 0; right < nums.size(); right++) {// 1. 右指针扩张,更新状态freq[nums[right]]++;// 或 sum += nums[right];// 2. 当不满足条件时,收缩左指针while (/* 不满足条件 */) {// 更新状态freq[nums[left]]--;// 或 sum -= nums[left];left++;}// 3. 更新结果result = max(result, right - left + 1);// 或 result += right - left + 1;}return result;}

问题识别技巧

什么时候用滑动窗口?

  1. 关键词:连续子数组、子串、最长/最短
  2. 限制条件:不超过K个某元素、至少包含K个某元素
  3. 数据特征:数组/字符串,通常需要找满足条件的区间

判断依据

// 如果是这些问题,考虑滑动窗口:
- "找到最短的连续子数组,其和 ≥ target"
- "找到最长的子串,其中最多有K个重复字符"
- "找到包含所有字符的最小子串"
- "统计满足条件的子数组个数"

关键决策点

1. 窗口何时扩张?

2. 窗口何时收缩?

  • 不满足题目条件时收缩
  • 关键:正确识别"不满足条件"的判断
    // 各种条件的判断:
    while (sum >= target)            // 最小长度:条件达成时收缩
    while (freq[c] > k)              // 最多K个重复:超过时收缩  
    while (unique_chars > k)         // 最多K种字符:超过时收缩
    while (zero_count > k)           // 最多翻转K个0:超过时收缩

3. 如何更新答案?

  • 最长问题ans = max(ans, right-left+1) 在while循环
  • 最短问题ans = min(ans, right-left+1) 在while循环
  • 计数问题ans += right-left+1 在while循环

经典问题分类与解法

类型1:固定条件窗口

// 问题:满足条件的最长/最短窗口
int minSubArrayLen(int target, vector<int>& nums) {int left = 0, sum = 0, ans = INT_MAX;for (int right = 0; right < nums.size(); right++) {sum += nums[right];while (sum >= target) {  // 满足条件时尝试收缩ans = min(ans, right - left + 1);sum -= nums[left];left++;}}return ans == INT_MAX ? 0 : ans;}

类型2:频率限制窗口

// 问题:最多K个重复字符/最多K种字符
int lengthOfLongestSubstringKDistinct(string s, int k) {
int left = 0, ans = 0;
unordered_map<char, int> freq;for (int right = 0; right < s.size(); right++) {freq[s[right]]++;while (freq.size() > k) {  // 超过K种字符freq[s[left]]--;if (freq[s[left]] == 0) freq.erase(s[left]);left++;}ans = max(ans, right - left + 1);}return ans;}

类型3:转换思维窗口

// 问题:从两端删除使和等于x → 找中间和为total-x的最长子数组
int minOperations(vector<int>& nums, int x) {int total = accumulate(nums.begin(), nums.end(), 0);int target = total - x;  // 关键转换int left = 0, sum = 0, max_len = -1;for (int right = 0; right < nums.size(); right++) {sum += nums[right];while (sum > target && left <= right) {sum -= nums[left];left++;}if (sum == target) {max_len = max(max_len, right - left + 1);}}return max_len == -1 ? -1 : nums.size() - max_len;}

教学要点

给初学者的建议

  1. 先画图理解

  2. 从暴力法思考

    暴力:O(n²) → 枚举所有子数组
    优化:滑动窗口 O(n) → 利用连续性
  3. 记住三个核心问题

  4. 从简单模板开始

    int left = 0;
    for (int right = 0; right < n; right++) {
    // 加入nums[right]
    while (/* 不满足条件 */) {
    // 移除nums[left]
    left++;
    }
    // 更新答案
    }

常见错误与调试

  1. 死循环:确保while循环条件最终能打破
  2. 漏掉答案:检查答案更新位置是否正确
  3. 边界错误:注意数组索引越界
  4. 初始值错误:ans初始化为合适值

练习题进阶路径

Level 1: 固定窗口大小问题
Level 2: 条件简单的可变窗口
Level 3: 需要统计频率的窗口
Level 4: 需要转换思维的问题
Level 5: 多条件复合窗口

一句话总结各类问题

  1. 最长无重复子串:字符频率 >1 时收缩
  2. 长度最小子数组:和 ≥ target 时收缩并记录
  3. 最大连续1的个数III:0的数量 >k 时收缩
  4. 乘积小于K的子数组:乘积 ≥k 时收缩,计数用窗口长度
  5. 水果成篮:水果种类 >2 时收缩
  6. 最小覆盖子串:需要额外记录匹配条件
  7. 替换后的最长重复字符:窗口长度-最大频率 >k 时收缩
  8. 字符串的排列:固定长度窗口+频率匹配

终极心法

滑动窗口 = 右指针探索 + 左指针维持合法性 + 适时记录答案

掌握这个心法,配合足够的练习,就能解决大部分滑动窗口问题。开始时多画图,多模拟,慢慢就会形成直觉。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询