LeetCode 525. 连续数组
题目描述

题意分析
数组只包含 0 和 1,要求返回两者数量相同的最长连续子数组的长度。把每个 0 的贡献记为
-1、每个 1 的贡献记为+1,区间和就是其中 1 的数量减去 0 的数量,因此目标变成最长的和为 0 的连续区间。
解法:前缀和 + 最早位置哈希表
核心思路
[!blue]
遍历到下标
i时,balance表示nums[0..i]中 1 与 0 的数量差。如果下标j处的前缀也有相同的数量差,两段前缀相减后,区间nums[j+1..i]的数量差就是 0,长度为i-j。反过来,任何合法区间的左端之前与右端处,前缀状态也必然相同。用
first记录每种balance第一次出现的下标。固定右端点i后,左侧相同状态出现得越早,区间越长;更晚的位置不可能带来更优答案,所以已有记录不能覆盖。依次考察每个右端点并取最大长度,就不会漏掉全局最优区间。数组之前的空前缀数量差为 0,位置记作
-1,因此先存入first[0] = -1。这样当前缀本身就平衡时,仍可统一用i-(-1)得到长度,不需要单独处理从下标 0 开始的区间。
解题步骤
- 初始化
balance = 0、最长长度ans = 0,建立哈希表并写入first[0] = -1。- 从左到右遍历:遇到 1 令
balance++,遇到 0 令balance--。- 若当前状态已出现,用
i-first[balance]更新ans;否则写入first[balance] = i,为后续右端点保留候选。- 遍历结束返回
ans。只有一个元素或所有元素相同时,状态不会重复,答案保持为 0。
代码实现
class Solution {
public int findMaxLength(int[] nums) {
Map<Integer, Integer> first = new HashMap<>();
// 数组之前的空前缀让从下标零开始的平衡区间也能计入。
first.put(0, -1);
int balance = 0;
int ans = 0;
for (int i = 0; i < nums.length; i++) {
balance += nums[i] == 1 ? 1 : -1;
Integer index = first.get(balance);
if (index != null) {
ans = Math.max(ans, i - index);
} else {
// 首次出现才记录,更早的位置为后续保留更长候选。
first.put(balance, i);
}
}
return ans;
}
}
func findMaxLength(nums []int) int {
// 数组之前的空前缀让从下标零开始的平衡区间也能计入。
first := map[int]int{0: -1}
balance, ans := 0, 0
for i, num := range nums {
if num == 1 {
balance++
} else {
balance--
}
if index, ok := first[balance]; ok {
if i-index > ans {
ans = i - index
}
} else {
// 首次出现才记录,更早的位置为后续保留更长候选。
first[balance] = i
}
}
return ans
}
复杂度分析
- 时间复杂度:$O(n)$。数组只遍历一次,哈希表查询和写入的均摊时间为 $O(1)$。
- 空间复杂度:$O(n)$。最坏情况下会记录 $n + 1$ 个不同的前缀状态。
关键点总结
[!green]
balance是前缀中两种元素的数量差,计算时改变贡献即可,无需修改输入数组。- 相同前缀状态之间恰好是一段合法区间,最早状态给出当前右端点的最长候选。
- 空前缀也必须参与比较,
0 -> -1使从数组开头开始的区间使用同一套公式。
易错点总结
[!yellow]
- 覆盖首次位置:会丢失最早的左侧前缀,使后续算出的区间变短。
- 遗漏
0 -> -1:会漏掉从下标 0 开始的合法区间。- 长度多加 1:表中保存的是区间左端点的前一个位置,公式应为
i - index。- 把 0 当作 0 累加:此时前缀只统计 1 的数量,已不再表示两类元素的数量差。
- 把 Go 的缺失键当作下标 0:必须用
index, ok区分“没有记录”和“首次位置正好为 0”。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 560. 和为 K 的子数组 | 中等 | 把0改成-1后,0与1等量等价于区间和为0;本题保存最早前缀位置求最长长度,而非统计数量。 |
| 1124. 表现良好的最长时间段 | 中等 | 同样把两类元素映射成正负贡献,原题要求区间和严格为正,本题要求等于0。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!