Skip to content

Beginner: foundations · Arrays and strings · topic 7 of 61 · 35 min

Prefix sums and difference arrays

Explanation

A prefix sum array stores the running totals of a list, so the sum of any range is one subtraction: O(1) per query after O(n) setup. A difference array does the reverse job: it applies many "add v to every item from l to r" updates in O(1) each, and one pass at the end gives the final values.

The problem: many range questions

Suppose a shop has daily sales for a year and the manager asks many questions like "what were total sales from day 40 to day 75?". Adding the days each time costs O(n) per question, so q questions cost O(n × q). With a year of data and thousands of questions, that adds up fast.

Prefix sums

A prefix is the start of a list. The prefix sum array holds, at position i, the sum of the first i items. It has n + 1 entries: prefix[0] = 0 (the sum of no items), prefix[1] = nums[0], prefix[2] = nums[0] + nums[1], and so on. Building it takes one pass, O(n) time and O(n) space.

The sum of nums[l] to nums[r] (both included) is then prefix[r + 1] − prefix[l]: the total up to r, minus the total before l. That is O(1) per question.

i012345
nums[i]31415—
prefix[i]0348914

Example: the sum of nums[1] to nums[3] is prefix[4] − prefix[1] = 9 − 3 = 6, which matches 1 + 4 + 1.

python
from itertools import accumulate

nums = [3, 1, 4, 1, 5]
prefix = list(accumulate(nums, initial=0))
print(prefix)                    # Output: [0, 3, 4, 8, 9, 14]
print(prefix[3 + 1] - prefix[1]) # Output: 6
itertools.accumulate with initial=0 builds the n + 1 prefix array in one line.
Tip: The extra 0 at the front removes a special case: the range starting at index 0 is simply prefix[r + 1] − prefix[0].

Preview: counting subarrays that sum to k

A subarray is a run of neighbouring items. A subarray from l to r sums to k exactly when prefix[r + 1] − prefix[l] = k, that is, when an earlier prefix equals the current prefix minus k. Keep a dictionary that counts how often each prefix has appeared, and you can count all such subarrays in one O(n) pass. Unlike the sliding window of Module 3, this works with negative numbers too.

python
def count_subarrays(nums, k):
    seen = {0: 1}      # the empty prefix
    running = 0
    count = 0
    for x in nums:
        running += x
        count += seen.get(running - k, 0)
        seen[running] = seen.get(running, 0) + 1
    return count

print(count_subarrays([1, 2, 3], 3))   # Output: 2  ([1, 2] and [3])
print(count_subarrays([1, -1, 0], 0))  # Output: 3
O(n) time and O(n) space. Module 3 covers hashing and this pattern in depth.

Difference arrays: many range updates

Now flip the problem: many updates, then one read. "Add v to every item from l to r" done directly costs O(r − l + 1) per update. A difference array diff of length n + 1 records only where a change starts and stops: diff[l] += v and diff[r + 1] −= v. After all updates, a running sum over diff gives the final value of every position. Each update is O(1), the final pass is O(n).

Think of it like this: Think of a train with stations 0 to n − 1. A group of passengers boards at station l and gets off after station r. You do not need to write their count at every station in between; you write "+group" where they board and "−group" just after they leave. Walking along the route and keeping a running total tells you how many seats are taken at each station.

Pivot index

The pivot index is where the sum of items to the left equals the sum to the right. With total = sum(nums), walk once and keep left_sum; at index i the right sum is total − left_sum − nums[i]. One pass, O(n) time and O(1) extra space.

Common mistakes

  • Off-by-one with the extra 0: the sum of l..r is prefix[r + 1] − prefix[l], not prefix[r] − prefix[l]. Fix: test with l = r and with l = 0.
  • Making the difference array length n instead of n + 1, so diff[r + 1] fails for the last index. Fix: allocate n + 1 and ignore the last slot.
  • Rebuilding the prefix array inside the query loop. Fix: build it once before answering any query.
  • Forgetting seen = {0: 1} in the subarray-sum pattern, which misses subarrays that start at index 0. Fix: the empty prefix counts once.
  • Using a sliding window for "sum equals k" when numbers can be negative. Fix: use prefix sums with a dictionary.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Prefix sums and difference arrays · 1/8

Prefix sums and difference arrays

This lesson teaches prefix sums and difference arrays, two small tricks that turn slow range work into constant time.

Prefer a video by a teacher? Find free videos on “prefix sum array difference array range update explained” on YouTube ↗

In real life: Seat counts on a long-distance train

A railway reservation system must know how many seats are booked between every pair of stations on a route, because one seat can be sold Delhi–Agra and again Agra–Bhopal. If each booking adds 1 to every station segment it covers, a long route with many bookings becomes slow. With a difference array, each booking is two updates (where the journey starts and just after it ends), and one pass gives the occupancy of every segment. This is a simplified picture of the idea; real systems add many more rules.

python
stations = 6
bookings = [(0, 2, 3), (1, 4, 2), (3, 5, 4)]   # (from, to, seats), both included
diff = [0] * (stations + 1)
for l, r, seats in bookings:
    diff[l] += seats
    diff[r + 1] -= seats
running, taken = 0, []
for i in range(stations):
    running += diff[i]
    taken.append(running)
print(taken)
Shows [3, 5, 5, 6, 6, 4]: the seats taken at each station.

Practice (0/4 done)

Task 1 · Code · Python

Write build_prefix(nums) that returns a list of length len(nums) + 1 where position i holds the sum of the first i items. Position 0 is 0. Example: build_prefix([3, 1, 4]) returns [0, 3, 4, 8].

Task 2 · Code · Python

Write range_sums(nums, queries). Each query is a tuple (l, r) of 0-based indexes, both included, with 0 ≤ l ≤ r < len(nums). Return a list with the sum of nums[l..r] for each query, in order. Build a prefix array once so the total cost is O(n + q). Example: range_sums([3, 1, 4, 1, 5], [(0, 4), (1, 3)]) returns [14, 6].

Task 3 · Code · Python

A train route has n stations, numbered 0 to n − 1, and every station starts with 0 seats booked. Each update (l, r, v) adds v seats to every station from l to r (both included); v can be negative for a cancellation. Write apply_increments(n, updates) that returns the final list of n values. Use a difference array so each update is O(1). Example: apply_increments(5, [(1, 3, 2)]) returns [0, 2, 2, 2, 0].

Task 4 · Code · Python

Write pivot_index(nums) that returns the LEFTMOST index i where the sum of the items left of i equals the sum of the items right of i (nums[i] itself is in neither). If there is none, return -1. O(n) time, O(1) extra space. Example: pivot_index([1, 7, 3, 6, 5, 6]) returns 3.

Quick quiz

Question 1

1.nums = [2, 5, 1, 4]. What is the prefix array (with a 0 in front)?

Question 2

2.With prefix of length n + 1, which gives the sum of nums[l..r] inclusive?

Question 3

3.n items and q range-sum queries. What is the total cost with a prefix array?

Question 4

4.To add v to every item from l to r with a difference array, you do…

Question 5

5.Why does the subarray-sum-equals-k pattern start with seen = {0: 1}?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

nums = [2, 5, 1, 4]. What is the prefix array (with a 0 in front)?

Answer: [0, 2, 7, 8, 12]

Running totals with 0 first: 0, 2, 2+5 = 7, 7+1 = 8, 8+4 = 12.

With prefix of length n + 1, which gives the sum of nums[l..r] inclusive?

Answer: prefix[r + 1] − prefix[l]

prefix[r + 1] covers items 0..r, and prefix[l] covers items 0..l − 1, which we remove.

n items and q range-sum queries. What is the total cost with a prefix array?

Answer: O(n + q)

O(n) to build once, then O(1) per query.

To add v to every item from l to r with a difference array, you do…

Answer: diff[l] += v and diff[r + 1] -= v

The change starts at l and stops just after r. The running sum later spreads it over l..r.

Why does the subarray-sum-equals-k pattern start with seen = {0: 1}?

Answer: So subarrays that start at index 0 are counted

A subarray from index 0 to r sums to k when the running total equals k, i.e. running − k = 0, the empty prefix.

For nums = [2, -2, 2] and k = 0, how many subarrays sum to k?Related

Answer: 2

[2, −2] and [−2, 2] both sum to 0; [2, −2, 2] sums to 2.

From Counting and grouping with hashing

In Kadane's algorithm, what does cur hold at index i?Related

Answer: The best sum of a run that ends exactly at i

cur answers the smaller question "best run ending here"; best tracks the overall answer.

From Maximum subarray: Kadane's algorithm

Why can the opposite-ends walk drop nums[left] when the sum is too small?Related

Answer: Because even with the largest value still in range, nums[right], the sum is too small

Every other partner for nums[left] is smaller than nums[right], so every pair with nums[left] is too small.

From Two pointers

Why must the numbers be positive for the shortest-subarray-with-sum-at-least-target window?Related

Answer: So removing an item from the left always lowers the sum, which makes "shrink while valid" correct

With negatives, removing an item can raise the sum, so the window can stop shrinking too early or miss the answer.

From Sliding window: variable size

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 3 more topics in “Arrays and strings” before its exam: Strings: building, comparing and counting, Maximum subarray: Kadane's algorithm, 2-D arrays (matrices).
  2. Module 2 exam: Arrays and strings · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Hashing, two pointers and sliding window. 61 topics left in the course before the final exam.

← Previous: Arrays: traversal and in-place updates