题目描述
思路解析动画文字版
朴素法能得到答案,但它把同一段连续 1 重复数了很多遍。题目只需要最长长度,所以更好的做法是边扫描边维护当前段长度。
代码里的不变量很简单:cur 只描述当前正在延伸的这段 1,ans 记住已经见过的最大值。每个数只看一次,既不会漏末尾连续 1,也不会重复数前面的段。
1. 读样例:找最长的一段连续 1:数组里有两段连续 1:开头 [1,1] 长度 2,结尾 [1,1,1] 长度 3。中间的 0 不能让两段合并,所以最终答案是 3。
2. 初始化:当前段长 = 0,历史最长 = 0:cur 记录当前正在延伸的这段 1 有多长,ans 记录历史见过的最长。还没读元素,两者都是 0。
3. i=0 读到 1:进入 if 分支:读第 0 个数 nums[0]=1,等于 1,进入 if 分支。当前段从这里开始,更新前当前段长仍是 0。
4. i=0 更新:当前段长 1,历史最长 1:cur 从 0 加到 1,当前段是 [0,0]。ans = max(0,1) = 1,历史最长第一次被记录为 1,最佳段标绿。
5. i=1 读到 1:进入 if 分支:读 nums[1]=1,等于 1,仍走 if 分支。它和前一个 1 相邻,当前段窗口延长到 [0,1],更新前当前段长是 1。
6. i=1 更新:当前段长 2,历史最长 2:当前段继续延长,cur 从 1 加到 2,段 [0,1]。ans = max(1,2) = 2,历史最长更新为 2,最佳段标绿为 [0,1]。
7. i=2 读到 0:进入 else 分支:读 nums[2]=0,不等于 1,走 else 分支。0 是断点,更新前当前段长还是 2,历史最长 2 不受影响。
8. i=2 更新:当前段长清零:0 切断了连续 1,cur 清零为 0,当前没有正在延伸的段。历史最长 ans 不会变小,仍是 2,绿色最佳段保留 [0,1]。
9. i=3 读到 1:进入 if 分支:读 nums[3]=1,进入 if 分支。它隔着一个 0,不能接到前段上,只能开启一段新的连续 1,更新前当前段长是 0。
10. i=3 更新:新段长 1,历史最长仍 2:新段开始,cur 从 0 加到 1,段 [3,3]。ans = max(2,1) = 2,没有超过历史最长,绿色最佳段仍是 [0,1]。
11. i=4 读到 1:当前段继续延长:读 nums[4]=1,当前段延长到 [3,4],cur 从 1 加到 2。ans = max(2,2) = 2,刚好追平历史最长,绿色最佳段未变。
12. i=5 读到 1:当前段长 3,超过历史:读 nums[5]=1,当前段延长到 [3,5],cur 从 2 加到 3。ans = max(2,3) = 3,首次超过历史,绿色最佳段切换到 [3,5]。
13. 扫描完毕:末尾连续 1 已结算:因为每读到一个 1 都立刻更新 ans,即使数组以 1 结尾也不会漏掉末段。六个数都已计数,返回 ans = 3。
14. 不变量回顾:cur 只描述当前段:贯穿全程的不变量:cur 永远等于当前正在延伸这段 1 的长度,ans 永远等于已见过的最长长度。每个数只看一次,总复杂度 O(n)。
15. 边界自检:全 0 与全 1:若数组全是 0,每步都走 else,cur 一直 0,ans 始终 0;若数组全是 1,cur 一路递增,ans 等于数组长度。本例答案 3 正确。
这题的可迁移点不是背代码,而是这三个动作:定义当前段、延续时更新、断点处重开。以后遇到“最长连续……”先套这个检查表。
参考代码
class Solution: def findMaxConsecutiveOnes(self, nums): ans = 0 cur = 0 for x in nums: if x == 1: cur += 1 ans = max(ans, cur) else: cur = 0 return ans复杂度
- 时间复杂度:O(n),数组里每个元素只扫描一次。
- 空间复杂度:O(1),只使用 ans 和 cur 两个计数变量。
易错点
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
所有奇数长度子数组的和
LeetCode 1588 · 简单 · 沿着 数组套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题