夯实算法-和为K的子数组

122 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的33天,点击查看活动详情

题目:LeetCode

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 *该数组中和为 k ***的连续子数组的个数示例 1:

输入: nums = [1,1,1], k = 2
输出: 2

示例 2:

输入: nums = [1,2,3], k = 3
输出: 2

提示:

  • 1<=nums.length<=21041 <= nums.length <= 2 * 10^4
  • -1000 <= nums[i] <= 1000
  • 107<=k<=107-10^7 <= k <= 10^7

解题思路

解法一:

利用前缀和来求解,首先构建一个前缀和数组presum,数组长度为n+1,即前面多一个值为0的头,由前缀和公式可得 nums[i,j] = presum[j+1]-presum[i] 由此两次循环便可遍历所有子集合的和

解法二:

可以利用一个哈希表来存储前缀和数组pre,只需要判断当前前缀和减去目标和的值,即正好从该位置数第前k个数,pre[j] = pre[i] - k,即num[i]到num[j]之前数的和为k。为防止出现数组一k开头的情况,首先向map中加入(0,1)的数据

代码实现

解法一:

public int subarraySum(int[] nums, int k) {
    // 前缀和数组
    int[] presum = new int[nums.length + 1];
    for (int i = 0; i < nums.length; i++) {
        presum[i+1] = nums[i] + presum[i];
    }
    // 统计个数
    int count = 0;
    for (int i = 0; i < nums.length; i++) {
        for (int j = i; j < nums.length; j++) {
            //注意偏移,因为我们的nums[2]到nums[4]等于presum[5]-presum[2]
            //所以这样就可以得到nums[i,j]区间内的和
            if (presum[j+1] - presum[i] == k){
                count++;
            }
        }
    }
    return count;
}

解法二:

public int subarraySum(int[] nums, int k) {
    int pre = 0,res = 0;
    Map<Integer, Integer> map = new HashMap<>();
    // 解决k = num[i]情况
    map.put(0,1);
    for (int i = 0; i < nums.length; i++) {
        pre += nums[i];
        if (map.containsKey(pre - k)){
            res += map.get(pre-k);
        }
        map.put(pre,map.getOrDefault(pre,0)+1);
    }
    return res;
}

运行结果

Snipaste_2022-12-31_15-15-09.png

复杂度分析

解法一:

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

解法二:

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

掘金(JUEJIN)  一起分享知识, Keep Learning!