拓冰建站拓冰建站
首页 / 资讯中心 / 正文

滑动窗口算法解决LeetCode 1004最大连续1问题

1. 题目解析与核心思路1.1 题目描述重述LeetCode 1004题最大连续1的个数 III的题目要求是给定一个二进制数组nums和一个整数k我们可以将最多k个0翻转为1返回数组中连续1的最大个数。举个具体例子输入nums [1,1,1,0,0,0,1,1,1,1,0], k 2 输出6 解释我们可以翻转最后两个0变成1这样就能得到连续的6个1。这个题目看似简单但考察了多个算法核心概念特别是滑动窗口技巧的应用。在实际面试中类似变形题经常出现在大厂技术面中比如字节跳动的算法面试就偏爱这类考察代码实现能力的题目。1.2 问题本质分析这道题的本质是在给定条件下寻找满足特定约束的最长子数组。具体来说数组元素只能是0或1允许的操作是将最多k个0翻转为1需要找到翻转后连续1的最长长度这个问题可以转化为找到一个最长的子数组其中最多包含k个0。因为我们可以把这些0都翻转为1从而得到全1的子数组。1.3 暴力解法思考最直观的解法是暴力枚举所有可能的子数组然后检查每个子数组中0的个数是否不超过k。对于长度为n的数组这样的时间复杂度是O(n²)当n较大时比如10^5这种解法显然不可行。# 暴力解法示例仅用于理解实际不可用 def longestOnes(nums, k): max_len 0 n len(nums) for i in range(n): zero_count 0 for j in range(i, n): if nums[j] 0: zero_count 1 if zero_count k: break max_len max(max_len, j - i 1) return max_len1.4 优化思路 - 滑动窗口滑动窗口(Sliding Window)是解决这类子数组/子串问题的经典技巧。其核心思想是维护一个窗口通过移动窗口的左右边界来寻找最优解避免不必要的重复计算。对于本题我们可以维护一个窗口[left, right]统计窗口中0的个数当0的个数超过k时移动左边界缩小窗口在整个过程中记录窗口的最大长度这种方法的时间复杂度可以优化到O(n)因为我们每个元素最多被访问两次被右边界包含一次被左边界排除一次。2. 滑动窗口解法详解2.1 基本滑动窗口实现下面是滑动窗口的标准实现代码def longestOnes(nums, k): left 0 max_len 0 zero_count 0 for right in range(len(nums)): if nums[right] 0: zero_count 1 while zero_count k: if nums[left] 0: zero_count - 1 left 1 max_len max(max_len, right - left 1) return max_len2.2 代码逐行解析让我们分解这段代码的关键部分初始化left窗口左边界初始为0max_len记录最大窗口长度zero_count当前窗口中0的个数右边界移动for right in range(len(nums))右边界逐步向右移动当遇到0时zero_count增加窗口调整while zero_count k当窗口中0的个数超过k时移动左边界直到0的个数不超过k如果左边界移过的是0则减少zero_count更新最大值每次右边界移动后计算当前窗口长度并更新max_len2.3 复杂度分析时间复杂度O(n)每个元素最多被访问两次空间复杂度O(1)只使用了常数个额外变量2.4 边界情况处理在实际编码中我们需要考虑一些边界情况全1数组如nums[1,1,1], k0应该返回3k大于0的个数如nums[0,0,1], k5应该返回3可以翻转所有0空数组nums[], k0应该返回0k0的情况即不允许翻转直接找最长连续1我们的滑动窗口实现已经正确处理了这些边界情况。3. 算法优化与变种3.1 滑动窗口的优化上面的实现中内层有一个while循环来移动左边界。实际上我们可以保证窗口只会扩大或平移不会缩小因此可以优化为if判断def longestOnes(nums, k): left 0 for right in range(len(nums)): if nums[right] 0: k - 1 if k 0: if nums[left] 0: k 1 left 1 return right - left 1这种实现更加简洁且保持了O(n)的时间复杂度。它的核心思想是把k当作信用点遇到0就消耗一个点当信用为负时移动左边界恢复信用最终窗口大小就是最大长度3.2 类似题目变种掌握这个算法后可以解决一系列类似问题最长连续子数组最多包含k个不同字符LeetCode 340和至少为K的最短子数组LeetCode 862替换后的最长重复字符LeetCode 424这些题目都可以用滑动窗口的思想解决只是判断条件有所不同。3.3 实际应用场景滑动窗口算法在实际开发中有广泛应用网络流量控制限制单位时间内的请求数量数据分析计算移动平均值或移动最大值生物信息学DNA序列模式匹配金融分析股票价格趋势分析4. 常见错误与调试技巧4.1 新手常见错误在实现滑动窗口时容易犯以下错误窗口边界处理不当忘记移动左边界左右边界移动条件错误计数更新不及时当左边界移动时忘记更新计数器0的计数与k的比较逻辑错误初始条件设置错误max_len初始值应为0而非1空数组情况未处理4.2 调试技巧当你的滑动窗口代码出现问题时可以打印窗口状态print(fleft{left}, right{right}, zeros{zero_count}, max{max_len})小规模测试用例先测试k0的情况再测试k大于0的个数的情况最后测试一般情况可视化窗口移动 在纸上画出数组和窗口移动过程有助于理解算法执行流程4.3 性能优化验证对于滑动窗口算法可以使用大规模数据验证其线性时间复杂度import time import random # 生成大规模测试数据1百万个元素 large_nums [random.randint(0,1) for _ in range(10**6)] k 100 start time.time() result longestOnes(large_nums, k) end time.time() print(fResult: {result}, Time: {end-start:.4f} seconds)对于O(n)的算法处理百万级数据应该在秒级完成。如果时间过长说明实现可能存在问题。5. 不同语言实现对比5.1 Java实现Java版本的滑动窗口实现public int longestOnes(int[] nums, int k) { int left 0; int maxLen 0; int zeroCount 0; for (int right 0; right nums.length; right) { if (nums[right] 0) { zeroCount; } while (zeroCount k) { if (nums[left] 0) { zeroCount--; } left; } maxLen Math.max(maxLen, right - left 1); } return maxLen; }5.2 C实现C版本的实现注意使用size_t处理数组索引#include algorithm int longestOnes(vectorint nums, int k) { size_t left 0; int max_len 0; int zero_count 0; for (size_t right 0; right nums.size(); right) { if (nums[right] 0) { zero_count; } while (zero_count k) { if (nums[left] 0) { zero_count--; } left; } max_len max(max_len, static_castint(right - left 1)); } return max_len; }5.3 JavaScript实现JavaScript版本适合前端开发者function longestOnes(nums, k) { let left 0; let maxLen 0; let zeroCount 0; for (let right 0; right nums.length; right) { if (nums[right] 0) { zeroCount; } while (zeroCount k) { if (nums[left] 0) { zeroCount--; } left; } maxLen Math.max(maxLen, right - left 1); } return maxLen; }5.4 语言特性对比不同语言实现中的注意事项Java数组使用.length属性Math.max用于比较大小C使用vector容器注意size_t和int的类型转换使用algorithm中的max函数JavaScript使用严格相等比较数组长度通过.length属性获取6. 进阶思考与扩展6.1 空间复杂度优化我们的解法已经是O(1)空间复杂度很难再优化。但如果题目变为找到具体是哪个子数组而非仅返回长度我们也只需要O(1)的额外空间来记录窗口位置。6.2 并行化思考对于超大规模数组如数十亿元素可以考虑将数组分割并行计算各部分的最大窗口然后合并结果。但需要注意边界处的合并逻辑。6.3 流式数据处理如果数据是以流的形式到来无法随机访问只能顺序读取一次滑动窗口算法仍然适用因为它的特性就是顺序处理数据。6.4 其他解法探索虽然滑动窗口是最优解但也可以思考其他方法前缀和二分查找计算前缀和数组其中prefix[i]表示前i个元素中0的个数对于每个i二分查找最大的j使得prefix[j]-prefix[i] k时间复杂度O(n log n)不如滑动窗口高效动态规划可以定义dp[i][j]表示前i个元素使用j次翻转的最长连续1但空间复杂度O(nk)不够高效这些方法虽然理论上可行但在实际面试中不如滑动窗口简洁高效。7. 面试技巧与实战建议7.1 面试解题步骤在面试中遇到这类题目时建议按以下步骤进行明确问题复述题目要求确认理解正确举例说明用具体例子演示输入输出暴力解法先提出暴力解法分析复杂度优化思路指出暴力解法的问题提出滑动窗口优化代码实现编写滑动窗口代码测试验证用多个测试用例验证代码复杂度分析明确说明时间空间复杂度扩展讨论讨论可能的变种和应用场景7.2 白板编码技巧在白板或共享编辑器上编码时先写伪代码勾勒算法框架边写边解释说明每部分代码的作用注意变量命名使用有意义的变量名预留空间为可能的修改留出空间测试用例写出几个测试用例及预期结果7.3 常见面试问题面试官可能会追问如何证明你的算法是正确的为什么滑动窗口的时间复杂度是O(n)如果数组中有负数或其他数字算法还适用吗如何修改算法返回具体的子数组而非仅长度如果k非常大如大于数组长度你的算法还高效吗准备好这些问题的答案展示你的思考深度。8. 刷题策略与学习建议8.1 系统性刷题方法要掌握滑动窗口这类算法建议分类刷题集中刷滑动窗口相关的题目由易到难从简单题目开始逐步挑战难题反复练习对同一题目多次实现直到熟练掌握总结模板提炼滑动窗口的代码模板举一反三思考每个题目的变种和应用8.2 滑动窗口问题特征识别适合滑动窗口解法的问题特征涉及连续子数组/子串问题要求找到满足条件的连续序列有明确的约束条件如最多k个0、不重复字符等需要优化长度通常要求最大或最小长度8.3 学习资源推荐LeetCode专题滑动窗口标签下的题目精选Top面试题中的滑动窗口问题算法书籍《算法导论》中的分治算法章节《编程珠玑》中的算法设计技巧在线课程Coursera上的算法专项课程LeetCode官方出品的算法课程8.4 个人练习建议根据我的刷题经验建议每日一题保持算法思维活跃度写解题报告记录每道题的思路和收获参与讨论查看其他人的解法学习优化技巧定期复习重做之前做过的题目巩固记忆模拟面试找朋友进行模拟面试练习表达滑动窗口是面试中非常高频的题型掌握它不仅能解决LeetCode 1004这样的题目还能应对许多变种问题。通过系统性练习和总结你可以在算法面试中游刃有余。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门