Prefix Sum — Stale Snapshot Breaks Range Queries
Updating array after building prefix sum gives wrong results — range queries may return negative sums.
- Prefix sums precompute cumulative totals in O(n) for O(1) range queries
- Range sum = prefix[right] - prefix[left-1] (0-indexed) or prefix[right+1] - prefix[left] (1-indexed)
- Hashmap variant counts/finds subarrays with target sum in O(n)
- 2D prefix sums use inclusion-exclusion for rectangle queries
- Biggest production pitfall: using prefix sum on mutable data — every update requires full rebuild
Imagine you're a cashier at the end of a very long checkout line, and someone asks 'what's the total of items 5 through 12?' Instead of adding them up on the spot every single time, you kept a running receipt where each line shows the total SO FAR. Now answering any range question takes one subtraction instead of a full recount. That running receipt IS a prefix sum array — a precomputed table of cumulative totals that turns expensive repeated work into instant lookups.
Range queries are everywhere in software: analytics dashboards summing sales between two dates, game engines calculating cumulative scores, financial systems aggregating transactions over arbitrary windows. The naive approach — loop over the subarray every time — works fine once, but collapses the moment you have thousands of queries on millions of data points. That's not a theoretical problem; it's the exact bottleneck that gets spotted in production and in whiteboard interviews alike.
Prefix sums solve a deceptively simple problem: given an array, answer 'what is the sum of elements from index i to index j?' in O(1) time, after a single O(n) preprocessing pass. The trick is that any subarray sum can be expressed as the difference between two prefix totals. Once you see that, you also start seeing it everywhere — subarray problems, sliding window variants, 2D grid queries, even problems disguised as hash-map lookups all share the same DNA.
By the end of this article you'll be able to: build a prefix sum array from scratch, apply it to solve classic interview problems like 'subarray sum equals K' and 'range sum query', extend the pattern to 2D matrices, and recognise the three or four disguises this pattern wears in real interviews so you can reach for it confidently the moment you see a range or subarray problem.
Building the Prefix Sum Array — The Foundation Everything Else Rests On
A prefix sum array (also called a cumulative sum array) is built by a single left-to-right pass where each cell stores the sum of all elements from index 0 up to and including that index. That's it. The magic comes from what you can do with it afterward.
If you want the sum of elements from index left to index right (inclusive), the formula is:
rangeSum = prefix[right] - prefix[left - 1]
Why does this work? Because prefix[right] already contains the total of everything from 0 to right. Subtracting prefix[left - 1] strips away everything before your window. What remains is exactly the subarray you care about.
The most common implementation uses a 1-indexed prefix array — meaning you shift the prefix array one position to the right and set prefix[0] = 0. This eliminates the need for an annoying left == 0 edge case check. It's a small discipline shift that pays off every time.
Time complexity for building: O(n). Space: O(n). Each query after that: O(1). That trade-off is almost always worth making when you know multiple queries are coming.
prefix[right+1] - prefix[left] with zero special cases. Interviewers notice clean, case-free code. Make it a reflex.Subarray Sum Equals K — The Classic FAANG Interview Problem
This is the problem that separates candidates who understand prefix sums deeply from those who just memorised the array-building step. The problem: given an array of integers (can include negatives) and a target K, count how many contiguous subarrays have a sum equal to K.
The brute force is O(n²) — try every start and end pair. The prefix sum + hash-map approach is O(n) and it's beautiful.
Here's the insight: a subarray from index i to j sums to K when:
prefix[j] - prefix[i-1] = K ⟹ prefix[i-1] = prefix[j] - K
So as you scan the array left to right, maintaining a running prefix total, you ask: 'how many times have I seen the value currentPrefix - K before?' Each time you've seen it is another valid subarray ending right here. You track the count of each prefix value seen so far in a HashMap.
Negative numbers? No problem — this approach handles them naturally because you're not making any assumptions about monotonicity. That's why it beats a sliding window for this specific problem.
prefixSumCount.put(0, 1) before the loop, you'll miss every subarray that starts at index 0. This is the single most common bug in this problem. It handles the case where the entire prefix up to index j equals K exactly — without it, you silently under-count.2D Prefix Sums — Extending the Pattern to Grid Problems
Once you're comfortable with 1D prefix sums, the 2D extension is a natural next step — and it appears in interviews more than you'd expect (image processing, grid-based games, matrix range queries).
The idea is the same: precompute a cumulative sum table where each cell prefix[row][col] stores the sum of ALL elements in the rectangle from (0,0) to (row, col). Then any rectangular subgrid sum can be computed in O(1) using inclusion-exclusion.
Building the table: prefix[r][c] = grid[r][c] + prefix[r-1][c] + prefix[r][c-1] - prefix[r-1][c-1]
The minus corrects for double-counting the overlap in the top-left corner.
Querying a rectangle from (r1, c1) to (r2, c2): sum = prefix[r2][c2] - prefix[r1-1][c2] - prefix[r2][c1-1] + prefix[r1-1][c1-1]
Again, inclusion-exclusion: start with the big rectangle, subtract the two strips above and to the left, then add back the corner you subtracted twice.
Using a 1-indexed table (padding row 0 and column 0 with zeros) makes the formula work uniformly for every cell, including the top-left corner of the grid — no special cases.
Recognising Prefix Sum Disguises — When the Problem Doesn't Say 'Sum'
The hardest prefix sum problems in interviews don't announce themselves. They look like completely different problems until you squint. Knowing the disguises is the real skill.
Disguise 1 — Binary arrays with equal counts: 'Find the longest subarray with equal numbers of 0s and 1s.' Replace every 0 with -1. Now you want the longest subarray summing to 0. That's a prefix sum + HashMap problem: find the earliest index where you've seen the same prefix value before.
Disguise 2 — Equilibrium index: 'Find an index where the sum of elements to the left equals the sum to the right.' Total sum minus prefix up to that point versus prefix up to that point — a single prefix scan solves it in O(n).
Disguise 3 — Product arrays: 'Build an array where each element is the product of all others.' Left prefix products and right suffix products, combined without division.
Disguise 4 — Difference arrays for range updates: When you need to apply an increment to a range [l, r] repeatedly, you record diff[l] += val and diff[r+1] -= val, then take one prefix sum at the end to get the final state. This is the inverse direction of prefix sums and turns O(n) per update into O(1) per update.
The pattern recognition test: does the problem involve subarrays, ranges, or cumulative aggregation? If yes, ask yourself if a prefix precomputation turns repeated work into O(1) lookups.
Prefix Sum with Modulo — Subarray Divisibility Problems
Another powerful disguise: problems that ask for subarrays whose sum is divisible by K. Example: 'Given an array of integers, return the number of subarrays with sum divisible by K.'
This is a direct extension of the hashmap pattern. Instead of tracking sum - K, you track sum % K. The key insight: if prefix[j] % K == prefix[i-1] % K, then the subarray from i to j has sum divisible by K. Because (prefix[j] - prefix[i-1]) % K = 0.
You seed the map with {0: 1} again — the empty prefix has remainder 0. Then for each prefix remainder, you count how many times it has appeared before.
This works for negative numbers too — just take care to normalise the modulo result to a non-negative value in languages where % can be negative (Java, C++). The formula: ((sum % K) + K) % K.
Time: O(n), Space: O(K) in the worst case (only K distinct remainders).
-3 % 5 returns -3, not 2. Always normalise: ((sum % k) + k) % k. Forgetting this causes the hashmap to miss matches and produce wrong counts. Python's % already returns a non-negative remainder.int mod(int a, int b) that handles negatives.The Week the Sales Dashboard Reported Negative Revenue
- Prefix sums are static snapshots — treat them like read-only caches.
- Always document the assumption: 'prefix sum is valid only if the underlying array has not changed since build.'
- In high-update environments, choose a BIT or segment tree from the start.
Key takeaways
{0: 1} in the map represents the empty prefix and is never optional.Common mistakes to avoid
4 patternsOff-by-one in the range formula
prefix[right] - prefix[left] instead of prefix[right+1] - prefix[left] with a 1-indexed table silenty excludes the element at right from your sum. Results are consistently one element short.prefix[right] - (left > 0 ? prefix[left-1] : 0)) or 1-indexed (prefix[right+1] - prefix[left]). Stick to it across your whole solution.Forgetting to seed the HashMap with {0: 1} before the loop
prefixSumCount.put(0, 1) as the very first line after declaring the map, before touching the input array.Using prefix sum on mutable data without rebuilding
Ignoring modulo normalisation for negative numbers
(-3) % 5 in Java/C++ returns -3 instead of 2. You'll miss valid matches and under-count.((sum % k) + k) % k. Write a small helper method to avoid forgetting.Interview Questions on This Topic
Given an array of integers that may contain negatives, find the total number of subarrays that sum to exactly K. What's your time complexity and why can't a sliding window solve this?
(currentSum - K) exists in the map — if so, we add its frequency to the count. Seed the map with (0,1) to account for subarrays starting at index 0. Time = O(n), space = O(n). Sliding window fails here because negative numbers mean the window sum doesn't behave monotonically — expanding and contracting the window cannot reliably find all subarrays with a given sum.Frequently Asked Questions
That's Coding Patterns. Mark it forged?
5 min read · try the examples if you haven't