Prefix Sum
Key idea — Build a slice prefix[i] = sum(arr[0..i-1]). Then any subarray sum arr[l..r] = prefix[r+1] - prefix[l] in O(1). Pair with a map to count subarrays whose sum equals a target.
When to use — Range sum queries, count subarrays with sum = k, subarray sum divisible by k, equilibrium index. Cue: "subarray sum" or "range query."
Pattern / template
go
// buildPrefix returns a prefix-sum slice where prefix[i] = sum(arr[0..i-1])
func buildPrefix(arr []int) []int {
prefix := make([]int, len(arr)+1)
for i, x := range arr {
prefix[i+1] = prefix[i] + x
}
return prefix
}
// rangeSum returns sum of arr[l..r] (0-indexed, inclusive) using prefix sums
func rangeSum(prefix []int, l, r int) int {
return prefix[r+1] - prefix[l]
}
// countSubarraysWithSum counts contiguous subarrays whose sum equals k
func countSubarraysWithSum(arr []int, k int) int {
count := 0
running := 0
seen := map[int]int{0: 1} // empty prefix
for _, x := range arr {
running += x
count += seen[running-k]
seen[running]++
}
return count
}Complexity
- Build: O(n) time, O(n) space.
- Each range query: O(1).
- Subarray count with map: O(n) time, O(n) space.
Pitfalls
- Off-by-one:
prefix[0] = 0(empty prefix) is essential — initialiseseen[0] = 1for the subarray count pattern. - The map stores prefix sums seen so far — do not add the current running sum to
seenbefore checking. - For 2D prefix sums (grid queries), extend to
prefix[i][j]with inclusion-exclusion.
Common follow-ups
- How does the prefix sum + hashmap pattern generalise to subarrays divisible by k?
- Why must you initialise
seen[0] = 1before the loop starts? - How would you extend this to a 2D grid for rectangular region sum queries?
- When would you use a difference array instead of a prefix sum?
Practice (LeetCode)
- LC 560 — Subarray Sum Equals K
- LC 303 — Range Sum Query - Immutable
- LC 304 — Range Sum Query 2D - Immutable
- LC 238 — Product of Array Except Self
- LC 525 — Contiguous Array