前缀和算法实战:连续数组与矩阵区域和
前缀和是处理区间求和问题的高效技巧,广泛应用于一维数组及二维矩阵的场景。本文将通过两道经典题目,深入讲解如何利用哈希表优化一维前缀和查询,以及如何构建二维前缀和矩阵实现 O(1) 的区域求和。
031 连续数组
题目描述:
给定一个二进制数组 nums,找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。
思路分析
暴力解法需要枚举所有子数组并统计 0 和 1 的数量,时间复杂度为 O(n²),在数据量较大时容易超时。我们需要寻找更优的解法。
核心转化在于将问题简化:如果我们将所有的 0 视为 -1,那么原问题就转化为'寻找和为 0 的最长子数组'。这与经典的「和为 K 的子数组」问题思路一致。
设 sum[i] 表示从索引 0 到 i 的前缀和。若存在两个位置 i 和 j(假设 i < j),使得 sum[j] == sum[i],则说明区间 (i, j] 内的元素之和为 0。因此,为了找到最长的满足条件的子数组,我们需要记录每个前缀和第一次出现的位置。
我们不需要显式地维护一个前缀和数组,只需使用哈希表存储 {前缀和值:首次出现的索引}。初始化时,哈希表中存入 {0: -1},代表前缀和为 0 出现在索引 -1 处(即空数组状态),这样当遍历到某个位置前缀和恰好为 0 时,可以直接计算长度。
代码实现
C++ 实现
class Solution {
public:
int findMaxLength(vector<int>& nums) {
unordered_map<int, int> hash;
// 初始化:前缀和为 0 时,对应索引为 -1
hash[0] = -1;
int sum = 0, ret = 0;
for (int i = 0; i < nums.size(); ++i) {
// 0 记为 -1,1 记为 1
sum += (nums[i] == 0 ? -1 : 1);
if (hash.count(sum)) {
// 如果当前前缀和之前出现过,更新最大长度
ret = max(ret, i - hash[sum]);
} else {
hash[sum] = i;
}
}
ret;
}
};


