LearnAbout

Prefix Sums

On this page
Quick reference
Build the prefix (or prefix-product) arrayO(n)
Range query, after the array is builtO(1)
Range query, re-summed from scratch (for comparison)O(n) per query
Difference-array range updateO(1) per update, O(n) to reveal final values
The Idea

Imagine an odometer. At each mile marker on a road, the odometer tells you the total distance from the start. You want to know the distance between mile marker 12 and mile marker 47. You do not drive the road again. You read the odometer at 47, read the odometer at 12, and subtract. A prefix-sum array is this odometer for a list of numbers. You compute the running total through each position one time. After that, the sum of any part [i, j] is one subtraction.

This is a good method because you compute the running totals one time and use them many times. To build the odometer readings costs one pass over the full road, which is O(n). But each question about a part of the road after that costs one subtraction, which is O(1). You do not add that part again each time someone asks.

This is the trade. It costs O(n) to build the prefix array one time. In exchange, each range-sum query after that is O(1). Without the prefix array, each query is O(n), because you add the range again each time someone asks.
Build It
The prefix array
// prefix[0] = 0                        // An empty range sums to 0. This padding prevents
                                          // a special case for "the range starts at 0".
// prefix[i] = prefix[i - 1] + arr[i - 1]  // the running total through the first i original elements
// prefix has length n + 1 for an original array of length n. It has one extra leading slot.
build the prefix array β€” O(n)
function buildPrefix(arr):
    n = length(arr)
    prefix = array of size n + 1
    prefix[0] = 0
    for i from 1 to n:
        prefix[i] = prefix[i - 1] + arr[i - 1]
    return prefix
range sum query [i, j] β€” O(1)
function rangeSum(prefix, i, j):
    // The sum of arr[i..j] is the total through j, minus the total before i.
    return prefix[j + 1] - prefix[i]
prefix[j + 1] is the total through index j. prefix[i] is the total through index i - 1, which is all elements before the range starts. The subtraction removes exactly the part outside [i, j].
prefix product β€” same trick, different operator
function buildPrefixProduct(arr):
    n = length(arr)
    prefix = array of size n + 1
    prefix[0] = 1                      // The empty product is 1, not 0.
    for i from 1 to n:
        prefix[i] = prefix[i - 1] * arr[i - 1]
    return prefix
The prefix idea is not only for addition. You can precompute any operation that lets you undo the earlier part. Multiplication with division is an example.
difference array β€” O(1) range updates, revealed with a prefix sum
function applyRangeAdd(diff, i, j, value):
    diff[i] += value          // Start to add 'value' from index i to the end.
    diff[j + 1] -= value        // Cancel that addition from index j + 1 to the end.

function reveal(diff):
    return buildPrefix(diff)   // A prefix sum over the diffs changes the start and stop
                                // markers back into the real per-index totals, in one O(n) pass.
This is the opposite of a prefix sum. A prefix sum precomputes totals to answer queries fast. A difference array records changes at the edges of a range, so you can do many range updates in O(1) each. You reconstruct the real values one time, at the end, with one prefix-sum pass.
Know It

The main idea is to move the cost. You pay one time, at the start, to build the array. Each query after that is arithmetic, not a walk.

OperationTimeSpaceWhy
Build the prefix (or prefix-product) arrayO(n)O(n)This is one pass over the input. It carries a running total (or product) forward one slot at a time.
Range query, after the array is builtO(1)O(1)A range sum is one subtraction of two totals that are already computed. You do not walk the range.
Range query, re-summed from scratch (for comparison)O(n) per queryO(1)Without a prebuilt prefix array, each query must add its own range again. This is the cost that the prefix array removes.
Difference-array range updateO(1) per update, O(n) to reveal final valuesO(n)Each update touches only two positions: the start of the range and one position after the end. You reconstruct the real per-index values one time, with one prefix-sum pass over the diffs.
Break It

Off-by-one on the extra leading zero

you index the prefix array as if it is the same length as the original

The prefix array has length n + 1, not n. prefix[0] = 0 is real padding, not a mistake. If you forget it, or forget the "+1" when you translate a range [i, j] into prefix[j + 1] - prefix[i], you make the most common prefix-sum bug. It moves every range sum by one element with no error.

Stale prefix sums after the data changes

the original array is modified after the prefix array is built

A prefix array is a snapshot. If arr[5] changes after buildPrefix runs, each prefix[i] for i > 5 is now wrong. rangeSum returns old answers with no error. You must build the full prefix array again after a change (O(n)), or use a structure made for updates. That structure is a different tool for a later phase.

Negative numbers break "the answer is just the range"

you find the best-sum range in an array that has negative numbers

With only positive numbers, the biggest-sum subarray is the full array. With negative numbers mixed in, this is no longer true. The best pair of prefix values to subtract is no longer clear from position alone. You must remember which prefix-sum values you have seen and where. This is why you pair prefix sums with a fast-lookup structure (the subject of the next topic) instead of a plain scan.

Use It
Range Sum Query - Immutable
Build the prefix array one time in the constructor. Each sumRange(i, j) call after that is prefix[j + 1] - prefix[i], with no loop.
Easy
Find Pivot Index
At index i, the left-side sum is prefix[i]. The right-side sum is (total - prefix[i] - arr[i]). Walk one time and compare these two values. Do not add either side again at each index.
Easy
Running Sum of 1d Array
This is the prefix array. The answer at index i is the running total through i, exactly as defined in Build It. You return it directly instead of using it for a range query.
Easy
Shuffle the Array
You do not need a running total here, only index math. The value for output position 2i is x[i]. The value for position 2i + 1 is y[i]. Walk one time and place the values directly. This is the same "array as addressable slots" idea from Arrays & Strings Fundamentals.
Easy
Contiguous Array
Treat each 0 as -1 and take a running sum (the prefix-sum idea from Build It). A subarray is balanced exactly when its running sum returns to a value that it had before. Remember the earliest index where each running-sum value first appeared (a lookup that the next topic formalizes). The gap between that first index and now is the length of a balanced subarray.
Medium
Product of Array Except Self
Use the prefix-product operation from Build It two times. A prefix-product pass (the product of all elements before i) multiplied by a suffix-product pass (the product of all elements after i) gives the answer at each index. You never divide.
Medium
Subarray Sum Equals K
Use a running sum again. A subarray sums to k exactly when an earlier running sum equals (current running sum - k). Keep a lookup of how many times each running-sum value has occurred so far (the fast-lookup structure that the next topic formalizes). Add its count each time you see a match.
Medium
Corporate Flight Bookings
Use the difference-array trick from Build It. Do not add seats to each day in [first, last]. Add one time at first and subtract one time at (last + 1). Run a prefix sum over that difference array at the end to reveal the real per-day totals in one pass.
Medium
Minimum Value to Get Positive Step by Step Sum
Walk the running sum like the build of the prefix array. Track its minimum value during the walk. The answer is 1 minus that minimum. Keep the answer at 1 or more.
Easy
Find the Highest Altitude
The running sum is the altitude at each point. Build the prefix array over the gain array and take its maximum value.
Easy