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

🌟 核心思想总结

三种双指针模式

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

📚 滑动窗口解题框架

通用模板

cpp 复制代码
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. 数据特征:数组/字符串,通常需要找满足条件的区间

判断依据

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

🔍 关键决策点

1. 窗口何时扩张?

  • 总是:右指针每次循环向右移动一步
  • 将新元素纳入窗口,更新相关统计

2. 窗口何时收缩?

  • 不满足题目条件时收缩

  • 关键:正确识别"不满足条件"的判断

    cpp 复制代码
    // 各种条件的判断:
    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:固定条件窗口

cpp 复制代码
// 问题:满足条件的最长/最短窗口
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:频率限制窗口

cpp 复制代码
// 问题:最多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:转换思维窗口

cpp 复制代码
// 问题:从两端删除使和等于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. 从简单模板开始

    cpp 复制代码
    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. 字符串的排列:固定长度窗口+频率匹配

🎁 终极心法

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

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

相关推荐
小武~3 小时前
Leetcode 每日一题C 语言版 -- 274 H-index
c语言·算法·leetcode
0 0 03 小时前
CCF-CSP 36-3 缓存模拟(cache)【C++】
开发语言·c++·算法
蒟蒻小袁3 小时前
Hot100--找到字符串中所有字母异位词
java·算法·leetcode·面试
kingmax542120083 小时前
高中数学教师资格面试试讲稿:《直线的位置关系(例2)》
线性代数·算法·面试·矩阵·教师资格
吃着火锅x唱着歌3 小时前
LeetCode 2909.元素和最小的山形三元组II
数据结构·算法·leetcode
小O的算法实验室3 小时前
2026年SEVC SCI2区,基于k均值聚类和自适应双群策略的粒子群算法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
程序员-King.3 小时前
day115—同向双指针—将x减到0的最小操作数(LeetCode-1658)
算法·leetcode·双指针
全栈工程师修炼指南3 小时前
Nginx | 负载均衡策略:一致性哈希算法实践
运维·算法·nginx·负载均衡·哈希算法
Jerryhut4 小时前
sklearn函数总结六——特征降维 压缩数据 - 特征提取(PCA&LDA)
人工智能·算法·机器学习·scikit-learn·sklearn