和为 K 的子数组

问题描述

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。

子数组是数组中元素的连续非空序列。

暴力法

枚举所有子数组情况,对其进行求和,很轻松可以找到和为 K 的子数组并统计次数。

int subarraySum(vector<int>& nums, int k) {
    int n = nums.size(), ret = 0;
    for (int i = 0; i < n; ++i) {
        int sum = 0;
        for (int j = i; j < n; ++j) {
            sum += nums[j];
            if (sum == k) {
                ret++;
            }
        }
    }
    return ret;
}

时间复杂度:O(n^2) 空间复杂度:O(1)

前缀和+哈希

实际上我们不用每次都重新计算子数组的和,可以观察到:子数组的和可以前缀和快速算出来,不用每次重新累加。

我们记录子数组前缀和为 pre[i],此时查询条件变成了找到满足 pre[j] - pre[i] == k (i <= j) 的元素对个数。

调换一下顺序:pre[i] = pre[j] - k,也就是说只要知道有多少一个 pre[i] 使得等式满足条件,就相当于有多少个子数组满足条件。

这样子我们就可以用一个字典记录前缀和及次数,边遍历边查询历史,直到遍历完整个数组。此时,只需要遍历一次。

int subarraySum(vector<int>& nums, int k) {
    int n = nums.size(), ret = 0;
    unordered_map<int, int> dict;
    dict[0] = 1;
    int pre = 0;
    for (auto x : nums) {
        pre += x;
        if (dict.find(pre - k) != dict.end()) {
            ret += dict[pre - k];
        }
        dict[pre]++;
    }
    return ret;
}

时间复杂度:O(n) 空间复杂度:O(n)