Skip to content
DSA Patterns

Learn

Prefix Sum

Prefix sums explained: O(1) range queries, the hash-map trick for counting subarrays with a target sum, and why it beats sliding window on arrays with negatives.

3 min readUpdated 2 Sept 2026

#Arrays#Hash Map#Prefix Sum#O(n)

Prefix sum is the pattern that picks up where sliding window stops. The moment an array can contain negative numbers, the window's shrink rule becomes unsound — and prefix sums plus a hash map is the replacement. It is also the answer to every "many range queries on a fixed array" question.

The idea

Build an array where prefix[i] is the sum of everything before index i:

nums31415prefix0031428394145the range we want
sum(1..3) = prefix[4] − prefix[1] = 9 − 3 = 6 — one subtraction, any range.

Then the sum of any range [i, j] is one subtraction:

sum(i..j) = prefix[j + 1] - prefix[i]
sum(1..3) = prefix[4] - prefix[1] = 9 - 3 = 6   →  1 + 4 + 1 ✓

O(n) to build, O(1) per query, forever. The leading zero is not decoration — it is what makes ranges starting at index 0 work without a special case, and leaving it out is the most common bug in this pattern.

class NumArray:
    def __init__(self, nums):
        self.prefix = [0]
        for x in nums:
            self.prefix.append(self.prefix[-1] + x)

    def sum_range(self, i, j):
        return self.prefix[j + 1] - self.prefix[i]

The counting trick

The version that actually gets asked: count the subarrays summing to k.

Rearrange the identity. A subarray ending at j sums to k exactly when prefix[j] - prefix[i] = k, i.e. when prefix[i] = prefix[j] - k. So while scanning, keep a count of every prefix seen so far and look up how many equal current - k:

def subarray_sum(nums, k):
    seen = {0: 1}          # the empty prefix — needed for subarrays starting at 0
    running = count = 0
    for x in nums:
        running += x
        count += seen.get(running - k, 0)
        seen[running] = seen.get(running, 0) + 1
    return count

One pass, O(n) time and space, and it is correct with negative numbers — which is the whole reason to reach for it over a window.

The {0: 1} seed is the same leading zero as before, wearing a different hat. Without it a subarray that starts at index 0 and sums to k is never counted.

The variants worth knowing

The same skeleton solves problems that look unrelated, by changing what you accumulate:

QuestionAccumulateLook up
Subarray Sum Equals Krunning sumrunning - k
Contiguous Array (equal 0s and 1s)+1/−1 running balancethe first index of this balance
Subarray Sums Divisible by Krunning % kthe same remainder
Product of Array Except Selfrunning product from each side— (prefix × suffix)
Number of Ways to Split Arrayrunning sumcompare against total - running

Note the second row: when the question asks for the longest such subarray rather than the count, store the earliest index each key was seen at and never overwrite it.

Product of Array Except Self is the same idea with multiplication: a prefix pass and a

suffix pass, multiplied, giving every element the product of everything but itself without division and in O(1) extra space.

Complexity

O(n) to build, O(1) per range query, O(n) space. The hash-map variants are one O(n) pass with O(n) space. Two-dimensional prefix sums generalise this to O(1) rectangle queries on a matrix with inclusion–exclusion, which is worth a look once the 1-D version is solid.

Mistakes that cost the round

  • Dropping the leading zero — or the {0: 1} seed. Both are the same off-by-one and

both produce answers that are correct except when the subarray starts at index 0.

  • Overwriting the first index when the question wants the longest subarray. For

counting you increment; for longest you keep the earliest.

  • Reaching for sliding window on an array with negatives. It looks like a window

problem and quietly returns wrong answers on inputs the samples do not cover.

  • Building the prefix array when you only need the running value. The counting variant

needs one integer, not an array.

What to drill

  1. Range Sum Query — Immutable — the bare prefix array.
  2. Subarray Sum Equals K — the hash-map trick.
  3. Product of Array Except Self — prefix and suffix, no division.
  4. Contiguous Array — the +1/−1 reframe.
  5. Subarray Sums Divisible by K — the same map keyed on a remainder.
  6. Number of Ways to Split Array — prefix against total.

All on the 22 DSA Patterns sheet.

Frequently asked

What is a prefix sum?

An array where each entry holds the sum of everything before it in the original array. With a leading zero, the sum of any range i..j is prefix[j + 1] - prefix[i] — O(n) to build once, then O(1) for every range query after.

When do I use prefix sums instead of a sliding window?

When the array can contain negative numbers, or when the question counts subarrays rather than optimising one. A sliding window needs the sum to move monotonically as the window grows; negatives break that. Prefix sums with a hash map make no monotonicity assumption.

Why seed the hash map with {0: 1}?

It represents the empty prefix before any element. A subarray that starts at index 0 and sums to k has running - k == 0 at its end, so without that entry the lookup finds nothing and the subarray is never counted.

Related