当前位置: 首页 > news >正文

LeetCode 1004:最大连续1的个数 III(滑动窗口) —— 题解

👋 欢迎阅读

一.题目

1004. 最大连续1的个数 III - 力扣(LeetCode)

🎯 欢迎来到「最大连续1的个数 III」题解之旅!本文将带你从“翻转最多 k 个 0 来获得最长连续 1”这一实际场景出发,深入理解滑动窗口(双指针)的灵活运用,并掌握如何通过维护窗口内 0 的个数不超过 k来高效求解最大窗口长度。

在开始之前,建议你先:

  • 了解题目背景:这是 LeetCode 1004 题,给定一个二进制数组nums和一个整数k,允许将最多 k 个 0 翻转成 1,求翻转后数组中连续 1 的最大个数。本质上,我们要找到一个最长的连续子数组,使得其中 0 的个数不超过 k,因为我们可以把这些 0 全部翻转为 1,从而得到一段全 1 的连续区间。

  • 明确学习目标:掌握滑动窗口核心流程——右指针right不断向右扩展,每遇到一个 0 就增加窗口内 0 的计数;如果计数超过k,则移动左指针left缩小窗口,直到窗口内 0 的个数 ≤ k;在此过程中不断更新窗口长度的最大值。理解为什么这种“右扩左缩”的策略能遍历所有可能的窗口,并保证找到最优解,同时熟练处理边界情况(如k=0或全为 1)。

  • 准备好环境:建议在本地 IDE 或 LeetCode 在线编辑器中打开代码,边看边运行,亲手验证示例(如nums = [1,1,1,0,0,0,1,1,1,0], k = 2输出6)。

本文将从问题转化、滑动窗口策略设计(0 计数约束)、窗口收缩条件到代码实现,层层递进。即使你对滑动窗口还不熟悉,我们也会从“维护一个最多包含 k 个 0 的窗口,并尝试拉长它”这一直觉出发,让你轻松抓住核心思想——窗口内 0 的数量是唯一限制条件,用双指针动态调整窗口,使窗口在满足条件时尽可能长。现在,让我们一起在二进制数组中滑动窗口,找出翻转后最长的连续 1 吧! 🔢📏

二.做题思路

一、问题分析(前置分析)

给定一个二进制数组nums和一个整数k,最多可以将k个 0 翻转为 1,求翻转后最长的连续 1 的子数组长度
核心观察:等价于寻找一个最长的子数组,使得其中 0 的个数不超过k。这符合滑动窗口的特性,因为窗口内 0 的个数随右指针扩展而增加,随左指针收缩而减少。


二、算法策略(滑动窗口)

  • 使用左右指针leftright维护窗口,变量zero记录窗口内 0 的个数。

  • 右指针right从 0 到 n-1 依次遍历,将nums[right]加入窗口,若为 0 则zero++

  • zero > k,则收缩左指针left++,同时若移出的是 0 则zero--,直到zero <= k

  • 每次调整后,更新最长窗口长度len = max(len, right - left + 1)

示例执行过程nums = [1, 1, 0, 0, 1, 1, 1, 0, 1, 1],k = 2):

步骤rightnums[right]入窗后zerozero > k?操作(收缩)当前窗口[left, right]窗口长度len更新
初始--0----0
1010-[0,0]11
2110-[0,1]22
3201-[0,2]33
4302-[0,3]44
5412-[0,4]55
6512-[0,5]66
7612-[0,6]77
8703左移:移除nums[0]=1zero仍为3;继续移除nums[1]=1,仍为3;移除nums[2]=0zero--变为2,left=3[3,7]57(保持)
9812-[3,8]67(保持)
10912-[3,9]77(保持)

最终len = 7,对应子数组[1, 1, 1, 0, 1, 1, 1](通过翻转两个 0 为 1,实际上原窗口[3,9]包含两个 0,翻转后全为1,长度为7)。


三、正确性说明(简单版本)

滑动窗口始终维护包含最多k个 0 的连续子数组。当窗口内 0 的个数超过k时,必须移动左指针直到满足条件,因为继续扩展右指针不会减少 0 的个数。这种“不满足就收缩”的策略,保证了在右指针固定的情况下,当前窗口是以该右端点为结尾的最长有效子数组。遍历所有右端点,记录最大值,即可得到全局最优解。由于每个元素最多入窗出窗一次,算法正确且高效。


四、实现细节(边界防护)

  • 初始化left = 0right = 0zero = 0len = 0

  • for (int right = 0; right < n; ++right)遍历:

    • nums[right] == 0zero++

    • while (zero > k)循环:若nums[left] == 0zero--left++

    • 更新len = max(len, right - left + 1)

  • n == 0直接返回 0(但题目保证 n ≥ 1)。

  • 时间复杂度 O(n),空间复杂度 O(1)。


五、返回值(目标映射)

返回len,即翻转后最长连续 1 的个数

三.代码

#include <iostream> #include <vector> #include <algorithm> using namespace std; class Solution { public: int longestOnes(vector<int>& nums, int k) { // 算法思路:滑动窗口(双指针) // 维护一个窗口 [left, right],使得窗口内 0 的个数不超过 k。 // 右指针不断向右扩展,每遇到一个 0 就增加 zero 计数; // 如果 zero 超过 k,则左指针右移,同时如果左指针移出的是 0,则 zero 减 1。 // 在每次调整后,更新窗口长度(right - left + 1),记录最大值。 int n = nums.size(); int zero = 0; // 当前窗口内 0 的个数 int len = 0; // 记录满足条件的最长窗口长度 // 双指针 left 和 right 定义窗口 [left, right] for (int left = 0, right = 0; right < n; right++) { // 入窗口:将 nums[right] 加入窗口 if (nums[right] == 0) { zero++; // 如果是 0,增加窗口内 0 的计数 } // 判断条件:如果窗口内 0 的个数超过 k,则需要收缩左边界 while (zero > k) { // 出窗口:将 nums[left] 移出窗口 if (nums[left] == 0) { zero--; // 如果移出的是 0,减少计数 } left++; // 左指针右移,缩小窗口 } // 更新结果:当前窗口满足条件(0 的个数 <= k),计算窗口长度 len = max(len, right - left + 1); } // 返回最长连续 1 的个数(即满足条件的最大窗口长度) return len; } }; int main() { // 测试用例:示例 1,期望输出 6 vector<int> nums = {1, 1, 1, 0, 0, 0, 1, 1, 1, 0}; int k = 2; Solution sol; int result = sol.longestOnes(nums, k); cout << result << endl; // 输出 6 return 0; }

四、易错点分析

难点一:for循环中right自增与窗口收缩的先后顺序

for (int left = 0, right = 0; right < n; right++) { if (nums[right] == 0) { zero++; } while (zero > k) { /* 收缩左边界 */ } len = max(len, right - left + 1); }

为什么容易困惑?
常规滑动窗口有时会在收缩后才更新len,但这里每次right扩展后立即入窗口,然后收缩,最后才更新长度。难点在于:len的更新是在while收缩之后,这意味着当前窗口始终是“合法”的(0 的个数 ≤ k),因此可以直接用right - left + 1
初学者可能误以为应该在入窗口前或收缩前更新长度,而本代码利用“先入后缩”的顺序,保证了更新时窗口已合法,这是理解上的关键转折点。


难点二:zero计数与left移出元素的关联

while (zero > k) { if (nums[left] == 0) { zero--; } left++; }

核心难点:
zero只记录当前窗口内 0 的个数,但left右移时,需要判断移出的元素是否为 0。这里容易混淆的是:left移动后,zero的减少仅发生在移出元素是 0 的情况下;如果移出的是 1,zero不变。
这个逻辑虽简单,但在调试时容易因为遗漏if判断而误以为left每动一次zero都要减 1,导致计数错误。理解“只减移出的 0”是掌握本算法的前提。


难点三:窗口长度的更新位置与最大值的维护

len = max(len, right - left + 1);

难点在于为什么放在这里?
因为经过while收缩后,窗口[left, right]一定满足zero ≤ k,此时窗口内所有 0 都可以通过翻转变成 1,所以当前窗口长度就是一个可行解
初学者可能认为只需在right扩展时更新一次,但若left发生了移动,窗口长度会变化,必须每次收缩后重新计算才能保证不遗漏更优解。这个位置强调了“每次调整后都取最大”的思想。

五、流程图

🎯 闭幕

🎉 恭喜你完成了「最大连续1的个数 III」问题的学习!

为了巩固知识并进一步拓展,建议你:

🚀动手实践
在 LeetCode 上提交代码,尝试不同的测试用例。

💡深入思考

  • 本题使用滑动窗口维护一个0 的个数不超过 k的区间,右指针不断扩展,当窗口内 0 的个数超过 k 时移动左指针。请问为什么当 zero > k 时,只需要移动一次左指针就可以继续?能否一次性跳到更远的位置?

  • 代码中当nums[right] == 0zero++,当nums[left] == 0zero--为什么只统计 0 的数量,而不需要统计 1 的数量?

  • 如果k = 0,问题退化为求最长连续 1 子数组,当前算法是否仍然正确?请验证。

  • 滑动窗口的时间复杂度为O(n),因为每个元素最多被左右指针各访问一次。如果数组长度n = 10^5,这个算法是否高效?

  • 本题要求最多翻转 k 个 0,相当于将窗口内的 0 视为“可以容忍的”。如果要求最多可以删除 k 个元素(不限定 0 或 1),使剩余部分中连续 1 最长,算法应如何调整?

📚延伸挑战

  • 如果问题改为最多翻转 k 个 1(使连续 0 最长),代码只需改动哪一处即可?

  • 如果数组中的数字不只是 0 和 1,而是包含多种取值(例如颜色分类中的 0,1,2),要求通过最多 k 次修改使得某种指定值连续最长,你会如何设计通用的滑动窗口框架?

如果你觉得本文对你有所帮助,欢迎:

👍点赞 / 收藏
👤关注作者,获取更多题解
💬留言交流你的疑问或优化思路


📌深入思考答案

  • 只移动一次左指针即可,因为滑动窗口的特性是每次右指针移动一步,左指针也至多移动一步,保证窗口始终合法,且每个位置作为窗口起点最多被考虑一次,这是 O(n) 的基础。一次性跳到更远位置虽可行,但会增加代码复杂度,且不利于维护窗口连续性。

  • 只需统计 0 的数量,因为窗口内 1 的数量 = 窗口长度 - 0 的数量,而翻转 0 后都变成 1,连续 1 的长度就是窗口长度,所以只要 0 的个数 ≤ k,窗口长度就是答案,无需额外统计 1。

  • k=0时,zero > 0 立即收缩左指针,窗口内始终保持 0 的个数为 0,即窗口内全为 1,算法退化为求最长连续 1 子数组,逻辑正确。

  • O(n) 对n=10^5非常高效,完全可接受。

  • 若允许删除任意 k 个元素,问题变为求删除 k 个元素后最长连续 1 子数组,本质上仍然是容忍 k 个非 1 元素,只需将判断条件改为统计窗口内非 1 的个数即可,逻辑相同。

🔍延伸挑战答案

  • 挑战1:只需将判断条件从nums[right] == 0改为nums[right] == 1zero改为统计 1 的个数即可,其余逻辑不变,即可求最长连续 0 子数组

  • 挑战2:将统计条件改为目标值以外的元素计数,即bad++nums[right] != target,收缩时判断bad > k,即可实现通用滑动窗口,适用于任意离散取值的数组。

祝你在算法之路上越走越稳,早日攻克每一道难题!下次见 🚀✨

http://www.jsqmd.com/news/1392449/

相关文章:

  • 老板一句“这个月线索为什么下降”,智能问数能查到什么?
  • 行业板块轮动因子实战从板块资金到因子建模的本地化Python全流程
  • Socket编程:客户端与服务器通信全解析(网络编程)
  • 代理(静态和动态)
  • 2026年消防设施操作员证报名入口,正规报考中心报名通道汇总 - 中科资质认证报考中心
  • 硬件测试内容之十三:LDO(芯片)
  • 郑州考公党必看!大学生毕业后档案存放流程!不用线下跑! - 实时传讯
  • 完整入门vscode-mermaid-preview:3步实现Mermaid图表实时预览与高清导出
  • 不同传感器前中后融合方案简介
  • 2026 年太原空调加氟空调出售,中央空调维修怎么预约? - LYL仔仔
  • Kettle数据迁移全复盘:从旧系统到新平台的8条实战经验清单
  • 手把手教你学 Simulink—— 整流器电磁干扰(EMI)滤波器设计与传导骚扰仿真
  • 【AI智能体速通】05.Agentic AI
  • Kimi LeetCode 3901. 好子序列查询 Python3实现
  • gcc编译器,以及源文件的翻译
  • 如何筛选靠谱的线上投票平台?2026年全场景通用投票工具深度评测
  • 从英语到斯瓦希里语,AI功能机的多语言适配怎么做?
  • POB汉化工具 PoeCharm 完整上手:3步安装与天赋技能配置实测
  • AI-实践 开发日志
  • 中国路况,中国速度——佳研 AI 让汽车碰撞仿真不再“卡脖子” - 2027品牌AI展
  • AI论文指令大合集:deepseek,kimi,豆包等各AI工具齐齐发力,轻松搞定论文写作难题
  • Databricks中用PySpark找到表里的最短唯一键
  • notepad-- 文件对比实战指南:3 步上手差异比对,5 个技巧让代码核查快 10 倍
  • TVA-具身智能最新进展(3):主动视觉感知提升实时性
  • 个体自发用 AI 提效已是职场常态,企业统一推进为何反而频频遇阻
  • 武汉江夏初中毕业生技术学校推荐 i3D AI 三维专业本地可参观试学 - 荆楚笔记
  • GHelper完整使用指南:如何用一个轻量小工具接管华硕笔记本性能控制
  • 合并报表系统有哪些?6家服务商能力对比与选型建议
  • Windows Defender 移除完整指南:三档移除深度与一次 ISO 实战演示
  • OpenBMC:WebUI 与后端接口交互流程