1. 题目解析与核心思路
这道LeetCode 1004题"最大连续1的个数 III"属于滑动窗口类经典问题。题目要求:给定一个由0和1组成的数组和一个整数k,我们可以将最多k个0翻转为1,返回数组中连续1的最大长度。
举个例子,对于数组[1,1,1,0,0,0,1,1,1,1,0]和k=2,我们可以翻转两个0变成1,这样就能得到长度为6的连续1序列。这个问题的核心在于如何在数组中找到一个最长的子数组,其中最多包含k个0。
滑动窗口算法非常适合解决这类"连续子数组"问题。它的基本思想是维护一个窗口,通过动态调整窗口的左右边界来寻找最优解。在这个问题中,窗口内的0的数量不能超过k,我们需要找到满足这个条件的最长窗口。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 滑动窗口算法详解
2.1 滑动窗口的基本原理
滑动窗口算法通过维护一个可变大小的窗口来遍历数组,通常使用两个指针(左指针和右指针)来标记窗口的边界。算法的核心在于如何高效地移动这两个指针:
- 右指针负责扩展窗口,寻找可能的解
- 左指针负责收缩窗口,优化当前解
对于本题,我们可以这样定义窗口的合法性:窗口内最多包含k个0。当窗口内的0数量超过k时,我们需要移动左指针来收缩窗口。
2.2 本题的具体实现步骤
具体到这道题目,滑动窗口的实现可以分为以下几个步骤:
- 初始化左指针left=0和右指针right=0
- 右指针right逐步向右移动,扩展窗口
- 每当遇到0时,计数器zero_count加1
- 当zero_count > k时,移动左指针left直到zero_count ≤ k
- 在每一步更新最大窗口大小max_len = max(max_len, right-left+1)
这种实现方式的时间复杂度是O(n),空间复杂度是O(1),非常高效。
3. 代码实现与逐行解析
3.1 C++实现代码
cpp复制class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int left = 0, right = 0;
int zero_count = 0;
int max_len = 0;
for (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, right - left + 1);
}
return max_len;
}
};
3.2 代码逐行解析
- 初始化变量:left和right指针都从0开始,zero_count记录当前窗口中的0数量,max_len记录最大窗口长度
- 外层for循环:右指针right遍历整个数组
- 遇到0时:zero_count增加1
- 当zero_count超过k时:进入while循环移动左指针left
- 如果左指针指向的是0,zero_count减1
- 无论是否指向0,左指针都向右移动
- 更新max_len:每次右指针移动后都计算当前窗口长度
- 返回最终结果max_len
注意:这里的while循环可以保证窗口内0的数量不超过k,即使k=0的情况也能正确处理。
4. 算法优化与边界条件
4.1 算法优化思路
虽然上述解法已经足够高效,但我们还可以做一些小优化:
- 当k大于等于数组中0的总数时,可以直接返回数组长度
- 可以使用if代替while,因为每次右指针移动只会引入一个0
优化后的代码如下:
cpp复制int longestOnes(vector<int>& nums, int k) {
int left = 0;
int max_len = 0;
int zero_count = 0;
for (int right = 0; right < nums.size(); right++) {
if (nums[right] == 0) {
zero_count++;
}
if (zero_count > k) {
if (nums[left] == 0) {
zero_count--;
}
left++;
}
max_len = max(max_len, right - left + 1);
}
return max_len;
}
4.2 边界条件处理
在实际编码中,我们需要考虑以下边界条件:
- 空数组输入:直接返回0
- k=0的情况:相当于找不含0的最长连续1子数组
- k大于等于数组中0的总数:可以翻转所有0,返回数组长度
- 全1数组:直接返回数组长度
- 全0数组且k>0:返回min(k, 数组长度)
5. 同类问题与扩展思考
5.1 类似滑动窗口问题
掌握这道题后,可以尝试解决以下类似问题:
- 无重复字符的最长子串(LeetCode 3)
- 最小覆盖子串(LeetCode 76)
- 长度最小的子数组(LeetCode 209)
- 替换后的最长重复字符(LeetCode 424)
- 最大连续1的个数(LeetCode 485)
5.2 问题变种与扩展
这道题可以有多种变种形式:
- 将0和1的角色互换:求最多翻转k个1为0后的最长连续0子数组
- 多维扩展:在二维矩阵中寻找满足条件的子矩阵
- 多条件限制:除了0的数量限制外,增加其他条件如子数组和的范围等
6. 实战技巧与常见错误
6.1 滑动窗口的调试技巧
在实际编码中,我总结了一些调试滑动窗口问题的技巧:
- 打印窗口状态:在每次指针移动后打印left、right和窗口内容
- 可视化跟踪:在纸上画出数组和指针移动过程
- 边界测试:专门测试k=0、k=数组长度等边界情况
6.2 常见错误与避免方法
新手在解决这类问题时容易犯以下错误:
- 混淆窗口收缩条件:确保只在zero_count > k时才移动左指针
- 更新max_len的时机:应该在每次右指针移动后都更新
- 指针越界:确保指针不会超出数组范围
- 初始化错误:max_len初始值应为0而非1
提示:在面试中,可以先口头解释算法思路,再写代码,这样即使代码有小错误,面试官也能理解你的思路。
7. 性能分析与复杂度证明
7.1 时间复杂度分析
这个算法的时间复杂度是O(n),其中n是数组长度。虽然内部有一个while循环,但每个元素最多被左指针和右指针各访问一次,所以总操作次数是2n,仍然是线性复杂度。
7.2 空间复杂度分析
算法只使用了固定数量的额外空间(几个int变量),所以空间复杂度是O(1)。
7.3 算法正确性证明
我们可以用循环不变量来证明算法的正确性:
- 初始化:窗口为空时,满足zero_count ≤ k
- 保持:每次右指针移动可能增加zero_count,但随后的while循环会确保zero_count ≤ k
- 终止:当右指针到达末尾时,我们检查了所有可能的窗口
因此,算法一定能找到最优解。
8. 不同语言实现对比
8.1 Python实现
python复制def longestOnes(nums, k):
left = zero_count = max_len = 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_len
Python实现更加简洁,利用了range和+=等语法糖。
8.2 Java实现
java复制public int longestOnes(int[] nums, int k) {
int left = 0, zeroCount = 0, maxLen = 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;
}
Java实现与C++类似,但要注意数组访问使用.length而不是.size()。
9. 实际应用场景
滑动窗口算法在实际中有广泛的应用:
- 网络流量控制:监控固定时间窗口内的请求数量
- 数据分析:计算移动平均值或移动最大值
- 生物信息学:DNA序列分析
- 金融分析:股票价格趋势分析
- 视频处理:关键帧检测
理解这类算法不仅能帮助通过技术面试,也能为解决实际问题提供思路。
10. 学习资源与进阶路径
10.1 推荐学习资源
- 《算法导论》中的分治和动态规划章节
- LeetCode探索卡片中的滑动窗口专题
- 各大高校的算法公开课(如MIT 6.006)
- 可视化算法学习网站(如VisuAlgo)
10.2 算法学习进阶路径
- 掌握基础数据结构:数组、链表、栈、队列、哈希表
- 学习基础算法:排序、搜索、递归
- 掌握高级算法思想:分治、贪心、动态规划、回溯
- 专项突破:滑动窗口、双指针、前缀和等技巧
- 参加编程竞赛:LeetCode周赛、Codeforces等
刷题时建议从简单题开始,逐步提升难度,每道题都要彻底理解,而不是单纯追求数量。这道"最大连续1的个数 III"作为中等难度题目,是检验滑动窗口掌握程度的很好例题。
