代码题——和为k的子数组

59 阅读1分钟

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

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

示例 1:

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

示例 2:

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

枚举法——时间复杂度大

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var subarraySum = function(nums, k) {
  let count=0
  for(let i=0;i<nums.length;i++){
    let sum=0
    for(let j=i;j<nums.length;j++){
      sum+=nums[j]
    }
    
    if(sum===k){
      count++
    }
  }
  
  return count
}

前缀和+哈希表优化

方法一的瓶颈是「每次都要枚举左端点」。我们可以用 前缀和 来优化。 我们定义 pre[i] 为 [0,..,i] 里所有数的和。那么子数组[j,..,i]的和为sum(j..i) = pre[i] - pre[j-1]。所以我们知道有多少个前缀和为pre[i]-k,就知道有多少个满足条件的子数组。

我们用一个哈希表 mp 来存储 前缀和出现的次数

  • key:某个前缀和
  • value:该前缀和出现的次数

遍历数组时:

  1. 计算当前前缀和 pre
  2. 判断 pre - k 是否在哈希表中,如果有,说明存在若干个子数组满足条件,把它们计入答案。
  3. 更新 mp[pre],记录这个前缀和出现的次数。
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var subarraySum = function(nums, k) {
  const map=new Map()
  map.set(0,1)
  
  let count=0,pre=0
  for(const x of nums){
    pre+=x
    
    if(map.has(pre-k)){
      count+=map.get(pre-k)
    }
    if(map.has(pre)){
      map.set(pre,map.get(pre)+1)
    } else {
      map.set(pre,1)
    }
  }
  
  return count
}