Q525 连续数组(前缀和 + 哈希)

来源:本题是 LeetCode 525,前缀和 + 哈希表的典型应用。这篇是我从 Obsidian 笔记整理过来的第一篇博客文章。

0 → -1,1 → 1

  • 子数组 0、1 数目相等 ⇨ 子数组所有元素相加 = 0
  • 问题转化:找最长区间和 = 0 的连续子数组

$$preSum[r] - preSum[l] = 0 \iff preSum[r] = preSum[l]$$

  • 若两个位置前缀和相等:区间 $[l, r-1]$ 和为 0,满足题意
  • 区间长度:$r - l$
1
2
3
4
// ❌ 错误写法:{n+1,0} 是初始化列表,生成【两个元素:n+1、0】
vector<int> preSum{n+1,0};
// ✅ 正确写法:(容量,初始值),开辟 n+1 个空间全部赋值 0
vector<int> preSum(n+1,0);

报错原因:vector 实际只有 2 个元素,循环访问 preSum[i+1] 下标越界 → heap-buffer-overflow 堆溢出

  1. 构造前缀和数组:$preSum[0]=0,preSum[i]=preSum[i-1]+(nums[i-1]==0?-1:1)$
  2. 哈希表 unordered_map<前缀和, 首次下标>,预先存入:map[0]=0(处理从数组开头就满足条件的子数组)
  3. 遍历前缀和:
    • 前缀和未出现:存入哈希(只存第一次下标,保证最长区间)
    • 前缀和已存在:计算区间长度 i - map[preSum[i]],更新最大值
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;

class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        int n = nums.size();
        vector<int> preSum(n + 1, 0); // 重点:圆括号初始化
        // 构建前缀和
        for(int i = 0; i < n; ++i){
            preSum[i+1] = preSum[i] + (nums[i]==0 ? -1 : 1);
        }
        unordered_map<int, int> mp;
        mp[0] = 0; // 初始化边界
        int res = 0;
        for(int i = 1; i <= n; ++i){
            auto it = mp.find(preSum[i]);
            if(it == mp.end()){
                mp[preSum[i]] = i; // 首次出现才保存
            }else{
                res = max(res, i - it->second);
            }
        }
        return res;
    }
};
  • 时间:$O(n)$,两次线性遍历
  • 空间:$O(n)$,前缀和数组 + 哈希表
  1. LeetCode560 和为 K 的子数组:pre[r] - pre[l] = K
  2. LeetCode974 和可被 K 整除子数组:同余前缀和配对

通用套路:前缀和做差转化区间条件 + 哈希存首次下标

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int findMaxLength(vector<int>& nums) {
    unordered_map<int,int> mp;
    mp[0]=-1;
    int sum=0,maxlen=0;
    for(int i=0;i<nums.size();i++){
        sum += nums[i]==1?1:-1;
        if(mp.count(sum)) maxlen=max(maxlen,i-mp[sum]);
        else mp[sum]=i;
    }
    return maxlen;
}

相关内容