【LeetCode】最大连续1的个数 III
文章目录
- 前言
 - 题目描述
 - 算法原理
 - 代码实现
 
前言
本文探讨了LeetCode题目1004(最大连续1的个数III)的解法。题目要求在二进制数组中翻转最多k个0后,找到连续1的最大长度。暴力解法通过两层循环枚举所有情况,但时间复杂度较高导致超时。优化解法采用滑动窗口技术,通过动态调整窗口左右边界,确保窗口内0的个数不超过k,从而高效计算最大连续1的长度。代码实现中,右指针遍历数组,左指针在0超标时移动收缩窗口,并实时更新最大长度。
题目链接:1004. 最大连续1的个数 III
题目描述
给定一个二进制数组
nums和一个整数k,假设最多可以翻转k个0,则返回执行操作后 数组中连续1的最大个数 。
示例 1:
输入: nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出: 6
解释: [1,1,1,0,0,1,1,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入: nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出: 10
解释: [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 10。
提示:
1 <= nums.length <= 10^5
nums[i]不是0就是1
0 <= k <= nums.length
算法原理
题目给了我们一个二进制数组 nums 和一个整数 k,并且题目中说最多可以翻转 k 个 0,它这个描述有点抽象,我们看下面的示例就好理解了,其实就是把 0 变成 1,注意题目那里说的是最多可以翻转 k 个 0,然后让我们返回执行操作后数组中连续 1 的最大个数 。
题目的意思很好理解,但问题是我们要怎么写代码?这道题乍一看很简单,但翻转 k 个 0 这个条件好像还不好处理,因为翻转的过程中可能和后面的 1 连起来,如示例 2,这种情况还比较复杂,直接下手不好处理。
这里我们可以转变一下思路,题目的要求我们还可以理解为:在一个二进制数组 nums 中,找出数组中连续 1 的最大个数,在这些 1 里面可以包含最多 k 个 0,想通了这一点之后,下面我们看看这道题具体要怎么解决?
解法一:暴力解法
 直接枚举所有可能得情况,先定义两个指针,从头开始遍历数组,遍历的过程中我们可以定义一个计数器 zero 来记录一下两个指针区间内的 0 的个数,当区间内 0 的个数大于 k 时,即找到一个符合条件的长度之后,我们再从第二个数开始,两层 for 循环就可以搞定。
代码如下:
class Solution 
{
public:int longestOnes(vector<int>& nums, int k) {int left = 0, right = 0, n = nums.size(), len = 0;for (int i = 0; i < n; i++){int size = 0, zero = 0;for (int j = i; j < n; j++){if (nums[j] == 1){size++;}else if (nums[j] == 0 && zero < k){zero++;size++;}else{break;}}len = len < size ? size : len;}return len;}
};
 
这种暴力枚举的解法比较容易想,代码也比较好写,但是会超时!

 解法二:
这里我们还是以示例 1 为例来模拟一下暴力枚举的过程,然后尝试进行优化,如下图所示:

当我们第一次循环走到两个指针区间内 0 的个数大于 k 时,暴力解法我们是让 L 走到第二个元素的位置,然后 R 从 L 的位置开始进行下一次循环,如下图所示:

到这里我们就发现问题了,第二次循环 L 虽然指向第二个元素了, 但 R 最终还是会走到与原来相同的位置,而且这一次循环肯定要比上一次循环得到的长度小。
这种情况下,R 最终还是会再次走到原先那个 0 的位置,所以 R 是完全可以不回退的,我们反而要让 L 继续向后走,直到两个指针区间内 0 的个数重新小于等于 k 为止

 分析到这里,我们大致的思路就出来了,由于两个指针都是往后走,所以我们可以用 “滑动窗口” 来做这道题!
这个分析过程还是比较容易的,但能不能写出来就要考验我们的代码能力了,我刚开始做这道题的时候,想到了要用滑动窗口来做,但是写的代码却怎么都不通过,后面还是看了别人写的代码之后才过了的
这里主要还是要想清楚我们模拟滑动窗口的过程,即进窗口、判断、出窗口以及滑动过程中的更新结果这几步【要注意更新结果这一步在什么时候更新具体要看题,不一定就是在最后】。
代码实现
首先定义两个指针,然后就是不断地进窗口、判断、出窗口以及更新结果
class Solution 
{
public:int longestOnes(vector<int>& nums, int k) {int left = 0, right = 0, n = nums.size(), len = 0, zero = 0;while (right < n){if (nums[right] == 0){zero++;}while (zero > k){if (nums[left++] == 0){zero--;}}len = max(len, right - left + 1);right++;}return len;}
};
 
进窗口时如果是 1 就直接进,如果是 0 则计数器 zero + 1,进了一个元素之后我们就判断一下计数器 zero 是否大于 k,只要大于,就循环的出窗口,直到计数器 zero 重新小于 k截止,出完窗口我们就更新一下结果,直到右指针出数组循环就停止。
完!
