Subarray Sum Equals K
Problem
nums (values may be negative) and an
integer k, count how many contiguous, non-empty subarrays add up to
exactly k.[1,1,1], k=2Output2[1,2,3], k=3Output21 ≤ n ≤ 2·104 · -1000 ≤ nums[i] ≤ 1000 · -107 ≤ k ≤ 107
Try it yourself
class Solution {
public int subarraySum(int[] nums, int k) {
}
}
(j..i] sums to k exactly when
prefix[i] - prefix[j] = k, i.e. prefix[j] = prefix[i] - k. So
while scanning, just ask: how many earlier prefixes equal sum - k?Instinct Repeated range-sum queries, or "count subarrays whose sum equals / divides K" with negatives allowed? Store running totals and subtract two of them.
Memory hack Any range is two prefixes apart - sum(i..j) = P[j+1] - P[i], so remember every P you've passed.
Open the Prefix Sum guide →Approaches
Brute ForceEnumerate & Re-sum Every Subarray
O(n3)Space O(1)Try every pair (start, end) and add up
nums[start..end] from scratch with a third loop. Count the pairs whose
sum equals k.
public int subarraySum(int[] nums, int k) { int count = 0; for (int start = 0; start < nums.length; start++) { for (int end = start; end < nums.length; end++) { int sum = 0; for (int m = start; m <= end; m++) sum += nums[m]; if (sum == k) count++; } } return count; }
Obviously correct, but cubic — far too slow for n = 2·104.
BetterRunning Sum per Start
O(n2)Space O(1)The sum of [start..end] is the sum of
[start..end-1] plus nums[end]. Extending the end pointer
and keeping a running total removes the innermost loop.
public int subarraySum(int[] nums, int k) { int count = 0; for (int start = 0; start < nums.length; start++) { int sum = 0; for (int end = start; end < nums.length; end++) { sum += nums[end]; if (sum == k) count++; } } return count; }
Quadratic, constant memory — ~2·108 steps at the limit.
OptimalPrefix Sum + Hash Map of Counts
O(n)Space O(n)Keep a running prefix sum and a map
prefix value → how many times seen. Every earlier prefix equal to
sum - k starts a subarray ending here with sum k. Seed
the map with {0: 1} so subarrays starting at index 0 count. Negatives
rule out a sliding window — sums are not monotonic.
public int subarraySum(int[] nums, int k) { Map<Integer, Integer> seen = new HashMap<>(); seen.put(0, 1); // empty prefix int count = 0, sum = 0; for (int num : nums) { sum += num; count += seen.getOrDefault(sum - k, 0); seen.merge(sum, 1, Integer::sum); } return count; }
Dry run[1,2,3], k=3
- i=01i23sum=1, need -2: 0
- i=112i3sum=3, need 0: +1 → 1
- i=2123isum=6, need 3: +1 → 2 ✔
One pass with O(1) average map operations.
k = 0 would count an empty subarray.At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | Enumerate & Re-sum Every Subarray | O(n3) | O(1) |
| Better | Running Sum per Start | O(n2) | O(1) |
| Optimal | Prefix Sum + Hash Map of Counts | O(n) | O(n) |
- Forgetting
seen.put(0, 1)— misses subarrays that start at index 0. - Using a sliding window — fails with negative numbers.
- Storing a boolean/index instead of a count — repeated prefixes each add a subarray.
- Ask early whether values can be negative; it decides window vs. prefix map.
- Write the identity
prefix[i] - prefix[j] = kbefore coding. - Same template solves divisible-by-K and binary-subarray variants.