Subarray Sum Equals K
Question
Given an array of integers nums and an integer , return the total number of contiguous subarrays whose sum equals exactly to .
Solution
Pattern
Prefix Sum with Hash Map (Frequency Map) Maintain a running sum (prefix sum) as you iterate through the array. Use a Hash Map to store the frequencies of all prefix sums seen so far. At each step, check if exists in the map; if so, add its frequency to your total count.
How to Identify
- The problem asks for contiguous subarrays (not subsequences).
- The problem is centered around a specific target sum ().
- The array can contain negative numbers or zeroes, strictly disqualifying Sliding Window / Two Pointers because the running sum does not monotonically increase or decrease.
- It asks for the count of such subarrays, requiring frequency tracking.
Description
Step-by-step explanation:
- Create a
HashMapto store<PrefixSum, Frequency>. - Seed the map with
(0, 1). This is a critical base case meaning "we have seen a prefix sum of 0 exactly once." It handles subarrays starting exactly from index 0 that sum to . - Initialize
sum = 0(running prefix sum) andcount = 0(total valid subarrays found). - Iterate through the array. For each element
num:- Add
numtosum. - Calculate the required prefix:
required = sum - k. - If
requiredexists as a key in the map, it means there are one or more historical prefixes we can "chop off" to leave a remaining subarray sum of exactly . Add the frequency ofrequiredtocount. - Finally, insert or update the current
suminto the map, incrementing its frequency by 1.
- Add
- Return the total
count.
The Intuition
This relies on fundamental prefix sum algebra. Let be the sum of elements from index to . The sum of a subarray from index to is calculated as:
We want . Substituting this into our equation: Rearranging for the unknown past prefix:
This means: as we stand at index (where our current sum is ), if we can look back and find how many times a sum of occurred in the past, that is exactly the number of valid subarrays ending at .
Complexity
| Label | Worst | Average |
|---|---|---|
| Time Complexity | ||
| Space Complexity |
Time Complexity
Iterating through the array takes . On average, Hash Map lookups and insertions are , giving time. In the absolute worst case (extreme hash collisions), map operations degrade to per step, leading to worst-case time complexity.
Space Complexity
In the worst case (where all running prefix sums are unique), the Hash Map will store distinct entries, yielding auxiliary space.
Code
class Solution {
public int subarraySum(int[] nums, int k) {
if (nums == null || nums.length == 0) return 0;
Map<Integer, Integer> prefixSumCount = new HashMap<>();
prefixSumCount.put(0, 1);
int sum = 0, count = 0;
for (int num : nums) {
sum += num;
count += prefixSumCount.getOrDefault(sum - k, 0);
prefixSumCount.put(sum, prefixSumCount.getOrDefault(sum, 0) + 1);
}
return count;
}
}Caveats
- The Sliding Window Trap: Do not attempt to use two pointers. If the array was
[1, -1, 1]and , expanding the right pointer makes the sum1, then0, then1. Because the sum fluctuates up and down, a left pointer wouldn't know whether to shrink or stay, breaking the sliding window invariant. - Order of Operations: You must check for
sum - kbefore you add the currentsumto the map. If , adding the current sum to the map first will falsely count the empty subarray (chopping off the current prefix from itself).
Concepts to Think About
- Frequency vs Earliest Index: In "Largest Subarray with Sum 0", we store
<Sum, EarliestIndex>because we want to maximize length. Here, we store<Sum, Frequency>because we want to count total occurrences. - Integer Overflow: If the array is massive and elements are large, the running sum might overflow a 32-bit
int. Using alongfor the running sum is safer in production systems, though LeetCode constraints for this specific problem usually keep it within 32-bit limits.
Logical Follow-up
Question: What if the problem guarantees that the array contains ONLY strictly positive integers?
Solution: You can abandon the Hash Map and use the Sliding Window (Two Pointers) approach. Use a left and right pointer. Expand right to increase the sum. If the sum exceeds , shrink left until it is . If it exactly equals , increment the count. This operates in strictly worst-case time and space.
Question: How would you find the number of subarrays whose sum is divisible by ? (LeetCode 974)
Solution: Instead of storing prefix sums, you store the frequencies of the prefix sum remainders modulo . If two prefix sums have the same remainder when divided by , the subarray strictly between them must be perfectly divisible by . (Note: you must adjust negative remainders in Java/C++ by doing (sum % k + k) % k). Time , Space .
Question: What if you need to find the number of submatrices (2D) that sum to ? (LeetCode 1074)
Solution: You can compress the 2D problem into a 1D problem. You iterate through all possible pairs of top and bottom rows (which takes ). For a fixed top and bottom row, you collapse the columns into a 1D array by summing them vertically. Then, you run this exact 1D Prefix Sum + Hash Map algorithm on that collapsed array. Total time: . Space: .