给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

1、暴力解法

优化算法大部分都是对暴力解法优化而来的,所以我认为先掌握暴力解法对理解优化算法是有帮助的。这道题的暴力解法就是依次遍历,需要注意的是设置int n = nums.size();会有效降低程序的复杂度,如果把nums.size()加入for循环则程序每次都会调用.size()。

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

2、 前缀和

思路:
假如存在区间[left,right],使得在[left,right]这个区间的子数组的和为k。换句话说,就是前right项和减去前left-1项和等于k,即前left-1项和等于前right项和减去k。
可以这样做,在扫描数组的同时,假设当前扫到第i位,记录它的前i项和sum,用该和减去k,即sum-k,判断sum-k是否为某个位置的前n项和,若是,更新统计量。
为什么呢,因为如果sum-k为某个位置的前n项和,即该和是已知的,是数组中实际存在的,如果我们用前i项和减k等于这个已知数,那么k也就是实际存在的。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        mp[0] = 1;
        int count = 0, pre = 0;
        for (auto& x:nums) {
            pre += x;
            if (mp.find(pre - k) != mp.end()) {
                count += mp[pre - k];
            }
            mp[pre]++;
        }
        return count;
    }
};

为什么要初始化初始化mp[0]=0呢?比如给出输入[3,-3], k = 0.如果初始化mp[0]=0则无法得到正确答案。

unordered_map:

优点: 因为内部实现了哈希表,因此其查找速度非常的快
缺点: 哈希表的建立比较耗费时间
适用处:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map