← 返回题目列表

最多翻转 K 个 0 后的最长连续 1 怎么用滑动窗口?

中等 第 25 / 27 题 更新于 2026/07/31
滑动窗口双指针连续区间

简化版

把“最多翻转 K 个 0”理解成窗口里最多允许有 K 个 0。

右指针扩张窗口并统计 0 的数量;如果 0 的数量超过 K,就移动左指针直到窗口重新合法。

每次窗口合法时,用 right - left + 1 更新最长长度。

详细版

这题是典型的“最长合法窗口”。

维护窗口 [left, right] 中 0 的个数 zeroCount。遍历 right,遇到 0 就加一;如果 zeroCount > k,说明翻转次数不够,需要不断移动 left,并在移出 0 时减少计数。

当窗口重新满足 zeroCount <= k 后,它表示一个可以通过翻转最多 k 个 0 变成全 1 的连续区间。

每轮更新答案即可。

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

完整版教学

一、题目条件怎么翻译成窗口约束

“最多翻转 K 个 0”不是让你真的修改数组,而是要求找到一个连续区间,区间里 0 的数量不超过 K。因为只要 0 的数量不超过 K,就能把这些 0 全部翻成 1。于是问题变成:找最长的连续子数组,使得其中 0 的数量 <= K

记忆钩子:翻转 K 个 0,本质是窗口内最多容纳 K 个坏字符。

二、为什么适合滑动窗口

当右指针向右扩张时,窗口里的 0 数量可能增加,约束可能被破坏。当左指针向右收缩时,窗口里的 0 数量可能减少,约束会变得更容易满足。这种“右扩可能变坏,左缩恢复合法”的结构,就是滑动窗口的经典形态。

操作zeroCount 变化窗口状态
右端加入 1不变不会更差
右端加入 0加 1可能超限
左端移出 0减 1可能恢复合法

窗口始终向右移动,不需要回头。

三、带数字推演

数组 [1,1,0,0,1,1,1,0]K = 1

right 到第 2 位 0:窗口 [1,1,0],zero=1,长度 3
right 到第 3 位 0:zero=2 超限,left 右移直到移出第一个 0
窗口变成 [0],zero=1
right 继续扩到 [0,1,1,1],长度 4

最长长度是 4,对应翻转一个 0 后得到连续 1。

四、代码模板

实现如下:

function longestOnes(nums, k) {
  let left = 0
  let zeroCount = 0
  let ans = 0
  for (let right = 0; right < nums.length; right++) {
    if (nums[right] === 0) zeroCount++
    while (zeroCount > k) {
      if (nums[left] === 0) zeroCount--
      left++
    }
    ans = Math.max(ans, right - left + 1)
  }
  return ans
}

这里用 whileif 更稳,因为一次右扩可能让窗口超限,需要持续恢复到合法状态。

五、和“无重复字符最长子串”的关系

这题和无重复字符最长子串都属于最长窗口,但约束不同。无重复字符维护的是每个字符最多出现 1 次;本题维护的是 0 最多出现 K 次。模板相同,差别在于窗口状态变量不同。

右扩:加入新元素,更新状态
非法:左缩,恢复状态
合法:更新最大长度

掌握这个抽象后,很多最长子数组题都能统一处理。

六、常见误区与追问

  • 误区:真的去翻转数组。 题目只问最长长度,统计窗口内 0 的数量即可。
  • 误区:窗口超限后只移动一次 left。 左端移出的是 1 时,zeroCount 不变,窗口仍可能非法。
  • 误区:更新答案放在窗口非法时。 答案必须来自合法窗口。
  • 追问:K 等于 0 怎么办? 算法仍然成立,此时就是找最长连续 1。
  • 追问:为什么复杂度是 O(n)? 两个指针都只向右走,每个元素最多进入和离开窗口一次。

这些点说明滑动窗口的本质是维护合法区间,而不是模拟操作。

七、加强记忆

这题记成“窗口里最多 K 个 0”。右指针负责扩张候选答案,zeroCount 记录需要翻转的 0 数量;一旦超过 K,左指针持续右移,直到 0 数量重新合规。每次合法后更新最大长度。这个模型还能迁移到“最多替换 K 个字符”“最多删除一个元素”等窗口约束题。