不能直接用双指针或滑动窗口,因为0和1数量相等不构成单调或固定约束;最优解是将0转为-1后转化为前缀和相等问题,用哈希表记录最早前缀和位置,时间o(n)、空间o(n)。

为什么不能直接用双指针或滑动窗口
因为子数组要求「0 和 1 的数量相等」,不是固定值、也不是单调关系,无法通过扩大/缩小窗口来维持某个不变量。暴力枚举所有子数组是 O(n²),但有更优解——关键在于把问题转换成「前缀和 + 哈希表找最早出现位置」。
- 把每个
0当作-1,1保持为1,那么子数组中 0 和 1 数量相等 ⇔ 子数组和为0 - 进一步:若前缀和
prefix[i]==prefix[j](i [i+1, j] 的和为 0 → 对应原数组中 0 和 1 数量相等
如何用 map 记录最早出现的前缀和位置
用 std::unordered_map<int int></int> 存 前缀和 → 最早下标,初始化时插入 0 → -1(表示空前缀和为 0,位置在 -1,方便处理从下标 0 开始的合法子数组)
- 遍历时计算当前前缀和
sum - 如果
sum已存在 map 中,说明从map[sum] + 1到当前下标是一段和为 0 的子数组,长度为i - map[sum] - 否则只记录第一次出现的位置(不覆盖),保证取到最长
int findMaxLength(vector<int>& nums) {
unordered_map<int int> first;
first[0] = -1; // 空前缀
int sum = 0, maxLen = 0;
for (int i = 0; i <h3>容易忽略的边界情况<ul>
<li>输入为空或只有一个元素:函数自然返回 <code>0</code>,无需特判</li>
<li>全是 <code>0</code> 或全是 <code>1</code>:前缀和单调增/减,不会重复,结果为 <code>0</code>,正确</li>
<li>第一个元素就构成合法子数组(如 <code>[0,1]</code>):靠 <code>first[0] = -1</code> 支撑,<code>i=1</code> 时 <code>sum=0</code> 已存在,长度 = <code>1 - (-1) = 2</code>
</li>
<li>使用 <code>unordered_map</code> 而非 <code>map</code>:避免 O(log n) 开销,整体保持 O(n)</li>
</ul>
<h3>时间与空间复杂度是否可接受<ul>
<li>时间:单次遍历,O(n),哈希查找均摊 O(1)</li>
<li>空间:最坏情况前缀和全不同,存 n+1 个键值对,O(n)</li>
<li>比二维 DP 或暴力前缀和表省空间,也比用 vector 存所有前缀和再二分查找更直接</li>
</ul>
</h3>
</h3>
<p>实际写的时候,别忘了头文件 <code><unordered_map></unordered_map></code> 和 <code><algorithm></algorithm></code>;如果编译器较老,<code>first.count(sum)</code> 可换成 <code>first.find(sum) != first.end()</code>。最易错的是忘记初始化 <code>first[0] = -1</code>,会导致漏掉以索引 0 开头的解。</p></int></int>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











