
1. 滑动窗口最大值问题解析1.1 问题理解与暴力解法滑动窗口最大值问题要求我们处理一个整数数组找出所有大小为k的滑动窗口中的最大值。最直观的解法是暴力遍历对每个窗口都扫描k个元素找出最大值。这种方法的时间复杂度是O(n*k)当n和k较大时效率极低。注意在LeetCode上暴力解法通常会因为超时无法通过所有测试用例必须寻找更优解。1.2 单调队列优化思路单调队列是解决这个问题的关键数据结构。它能在O(1)时间内获取当前窗口的最大值整体算法复杂度降到O(n)。核心思想是维护一个双端队列队列中的元素始终保持单调递减的顺序。实现细节队列中存储的是数组元素的索引而非值本身方便判断元素是否还在当前窗口内每次移动窗口时移除不在窗口范围内的元素从队首新元素入队前从队尾移除所有比它小的元素保持队列单调性1.3 C实现详解class Solution { public: vectorint maxSlidingWindow(vectorint nums, int k) { dequeint dq; vectorint res; for(int i 0; i nums.size(); i) { // 移除不在窗口内的元素 if(!dq.empty() dq.front() i - k) dq.pop_front(); // 维护单调递减队列 while(!dq.empty() nums[dq.back()] nums[i]) dq.pop_back(); dq.push_back(i); // 当窗口形成后开始记录结果 if(i k - 1) res.push_back(nums[dq.front()]); } return res; } };1.4 复杂度分析与边界情况时间复杂度O(n)每个元素最多入队出队一次 空间复杂度O(k)队列最多存储k个元素边界情况处理空数组输入k1的情况k等于数组长度的情况数组中所有元素相同的情况2. 最小覆盖子串问题解析2.1 问题理解与滑动窗口思路最小覆盖子串需要在字符串s中找到一个最短的子串包含字符串t的所有字符包括重复字符。滑动窗口是解决这类子串问题的标准方法。关键点使用哈希表记录t中每个字符的出现次数维护一个滑动窗口动态扩展和收缩使用计数器跟踪当前窗口中满足条件的字符数量2.2 哈希表与双指针实现class Solution { public: string minWindow(string s, string t) { unordered_mapchar, int need, window; for(char c : t) need[c]; int left 0, right 0; int valid 0; int start 0, len INT_MAX; while(right s.size()) { char c s[right]; if(need.count(c)) { window[c]; if(window[c] need[c]) valid; } while(valid need.size()) { if(right - left len) { start left; len right - left; } char d s[left]; if(need.count(d)) { if(window[d] need[d]) valid--; window[d]--; } } } return len INT_MAX ? : s.substr(start, len); } };2.3 优化技巧与注意事项使用数组代替哈希表当字符集较小时如ASCII可以用128或256大小的数组提高效率提前终止当找到len等于t.length()的子串时可以直接返回边界处理s比t短的情况直接返回空串重要提示window[c] need[c]的判断是关键确保字符数量足够但不多余3. 合并区间问题解析3.1 问题理解与排序思路合并区间问题要求将重叠的区间合并。解决这个问题的关键在于先对区间进行排序这样相邻的区间才有可能重叠。排序策略按照区间起始点升序排序如果起始点相同可以按结束点升序或降序影响不大3.2 贪心算法实现class Solution { public: vectorvectorint merge(vectorvectorint intervals) { if(intervals.empty()) return {}; sort(intervals.begin(), intervals.end(), [](const vectorint a, const vectorint b){ return a[0] b[0]; }); vectorvectorint merged; merged.push_back(intervals[0]); for(int i 1; i intervals.size(); i) { if(merged.back()[1] intervals[i][0]) { merged.back()[1] max(merged.back()[1], intervals[i][1]); } else { merged.push_back(intervals[i]); } } return merged; } };3.3 复杂度分析与变种问题时间复杂度O(nlogn)主要由排序决定 空间复杂度O(logn)或O(n)取决于排序实现变种问题求区间交集而非合并插入新区间并合并删除被完全包含的区间4. 滑动窗口问题通用解法总结4.1 滑动窗口问题分类固定长度窗口如滑动窗口最大值可变长度窗口如最小覆盖子串计数类问题如包含所有字符的最短子串极值问题如和大于等于target的最短子数组4.2 滑动窗口模板代码// 可变窗口模板 void slidingWindow(string s) { unordered_mapchar, int window; int left 0, right 0; while(right s.size()) { // 增大窗口 char c s[right]; window[c]; // 满足条件时收缩窗口 while(window needs shrink) { char d s[left]; window[d]--; } } } // 固定窗口模板 vectorint fixedSlidingWindow(vectorint nums, int k) { vectorint res; dequeint dq; for(int i 0; i nums.size(); i) { // 维护窗口大小 if(!dq.empty() dq.front() i - k) { dq.pop_front(); } // 维护单调性或其他条件 while(!dq.empty() nums[dq.back()] nums[i]) { dq.pop_back(); } dq.push_back(i); // 记录结果 if(i k - 1) { res.push_back(nums[dq.front()]); } } return res; }4.3 常见错误与调试技巧窗口边界处理不当确保左右指针移动逻辑正确条件判断错误特别是等于号是否应该包含哈希表更新时机错误在指针移动前后要正确更新状态初始状态处理特别是第一个窗口的特殊处理调试建议打印窗口状态和关键变量使用小测试用例手动模拟检查边界条件处理5. C实现中的性能优化5.1 容器选择与优化deque vs listdeque在两端操作效率更高unordered_map vs map前者查找更快但不保证顺序vector预分配提前reserve避免多次扩容5.2 内存与速度权衡使用数组代替哈希表当键值范围有限时减少不必要的拷贝使用引用和移动语义内联小函数如比较函数和简单getter5.3 多解法性能对比以滑动窗口最大值为例暴力法O(n*k)时间O(1)空间单调队列O(n)时间O(k)空间分块预处理O(n)时间O(n)空间实际测试中单调队列在大多数情况下表现最好但当k特别大时分块方法可能更优。6. 算法思维训练建议6.1 同类问题扩展练习滑动窗口相关长度最小的子数组(209)字符串的排列(567)最大连续1的个数III(1004)区间问题插入区间(57)会议室II(253)无重叠区间(435)6.2 解题思路培养先理解问题明确输入输出考虑暴力解法及其复杂度寻找重复计算或可以优化的部分选择合适的数据结构编写伪代码验证思路实现并测试边界情况6.3 调试与验证方法小测试用例手动验证打印中间结果对比暴力解法的输出使用LeetCode的测试用例分析失败案例的特殊性在实际编码中我发现理解滑动窗口问题的关键在于明确三点何时扩展窗口、何时收缩窗口、如何更新结果。这需要仔细分析问题条件和状态转移关系。