Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Structures & Algorithms (DSA)  Module 2 – Arrays, strings and in-place techniques

Prefix sums and difference arrays

Answer any range total in O(1) after one pass, apply many range updates in O(n + q) with a difference array, and know when each one fits.

  • Beginner
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free core, Pro depth: 8 of 25 minutes free
Free: Range totals in one subtraction · The zero in front and half-open ranges · Many range updates with a difference array · 2 more sections. With a pass: 3 in-depth parts and 3 interview answers, about 17 minutes.

What you will learn

  • Answer range-sum queries in O(1) after O(n) preprocessing
  • Apply many range increments in O(n + q) with a difference array
  • Extend prefix sums and difference arrays to two-dimensional grids
  • Choose a type for prefix totals from the largest total they can reach

Before you start

A prefix sum array stores running totals: prefix[0] = 0, and prefix[i] is the total of the first i items of an array. Building it takes one pass, O(n). After that, the total of any range a[first..last] is one subtraction, prefix[last + 1] − prefix[first], because both prefixes contain the items before first and they cancel. A difference array runs the idea backwards. To add v to every item of a range, record +v where the range starts and −v just after it ends; once all the updates are recorded, one running total turns the marks into the final values, so q range updates cost O(n + q) instead of up to n × q. Prefix sums suit many range questions about data that does not change; difference arrays suit many range updates made before anyone reads the values.

Range totals in one subtraction

A year of daily rainfall makes a good test: 365 numbers and endless questions such as “how much fell between day 151 and day 242?”. Adding up the range each time costs up to 365 additions per question. With prefix sums the year is added up once, and every question after that is answered by one subtraction:

Rainfall over any range of days, from one prefix array Python · prefix/rainfall.py
"""Rainfall totals for any range of days, from a prefix array of a year of synthetic daily readings.

prefix[i] is the total of the first i days, with prefix[0] = 0, so the total of days first to last (counting from 0,
both included) is prefix[last + 1] - prefix[first]. The program also counts the additions each method needs.
"""

import random

rng = random.Random(365)
rain_mm = [rng.choice([0, 0, 0, 1, 2, 4, 7, 12, 25, 40]) for _ in range(365)]  # synthetic daily rainfall in mm

prefix = [0] * (len(rain_mm) + 1)
for i, mm in enumerate(rain_mm):
    prefix[i + 1] = prefix[i] + mm
print("first ten days:", rain_mm[:10])
print("prefix[0..10]: ", prefix[:11])

queries = [(0, 364), (151, 242), (10, 10), (300, 330)]
for first, last in queries:
    total = prefix[last + 1] - prefix[first]
    assert total == sum(rain_mm[first : last + 1])
    print(f"days {first:>3} to {last:>3}: {total:>5} mm  = prefix[{last + 1}] - prefix[{first}]")

q = 100_000
print(f"{q:,} random range queries: up to {q * 365:,} additions one by one, {365 + q:,} operations with the prefix array")

Output

first ten days: [2, 0, 2, 40, 1, 25, 7, 40, 7, 25]
prefix[0..10]:  [0, 2, 2, 4, 44, 45, 70, 77, 117, 124, 149]
days   0 to 364:  3065 mm  = prefix[365] - prefix[0]
days 151 to 242:   668 mm  = prefix[243] - prefix[151]
days  10 to  10:     0 mm  = prefix[11] - prefix[10]
days 300 to 330:   266 mm  = prefix[331] - prefix[300]
100,000 random range queries: up to 36,500,000 additions one by one, 100,365 operations with the prefix array

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 rainfall.py

Each answer is checked against adding up the range directly. The last line puts the saving in numbers: 100,000 questions would cost up to 36.5 million additions one by one, against 100,365 operations with the prefix array. Python can build the array in one call, list(itertools.accumulate(rain_mm, initial=0)); the loop shows what that call does.

The zero in front and half-open ranges

The leading 0 is what keeps the formula free of special cases. With it, prefix[i] is the total of the half-open range a[0:i], the first i items, and a range that starts at day 0 is simply prefix[last + 1] − prefix[0]. Without it, the array would hold the totals of a[0..i], and a range starting at 0 would need prefix[−1], which Python does not reject: it quietly reads the last entry and returns a wrong answer. Thinking in half-open ranges, start included and end excluded, removes the other classic slip: the total of a[first..last] uses last + 1.

Many range updates with a difference array

Now turn the problem round. A hall has 8 seats, bookings reserve ranges of seats, and at the end you need the number of bookings covering each seat. Adding 1 to every seat of every booking costs as much as the bookings are long. Difference arrays record each booking with two marks instead:

Two seat bookings leave marks +1, +1, −1 and −1 in a difference array; a running total of the marks gives the count for each seat.Two bookings on 8 seats: seats 1 to 4, and seats 3 to 6Marks: +1 where a booking starts, −1 just after it endsRunning total of the marks: bookings per seatseat 00seat 1+1seat 20seat 3+1seat 40seat 5−1seat 60seat 7−1seat 00seat 11seat 21seat 32seat 42seat 51seat 61seat 702 marks per booking, O(1) eachone pass, O(n)

Two marks per booking, then one running total

Text description of the diagram

The diagram shows how a difference array records two bookings on eight seats, from top to bottom.

  • The bookings reserve seats 1 to 4 and seats 3 to 6.
  • Each booking leaves two marks: +1 at its first seat and −1 at the seat just after its last one. The marks are +1 at seat 1, +1 at seat 3, −1 at seat 5 and −1 at seat 7; every other seat has 0. Recording a booking takes two additions, however many seats it covers.
  • A running total of the marks, from seat 0 to seat 7, gives the number of bookings that cover each seat: 0, 1, 1, 2, 2, 1, 1 and 0. Seats 3 and 4 are in both bookings. That one pass takes O(n) time for n seats, so q bookings cost O(n + q) in all.
Four bookings recorded as marks, then one running total Python · difference/bookings.py
"""Applies range bookings to seats with a difference array, then checks the result against doing them one by one.

A booking (first, last) adds 1 to every seat from first to last. The difference array records only its two ends:
+1 at first and -1 just after last. One prefix sum over the marks then gives every seat's count.
"""

SEATS = 8
bookings = [(1, 4), (3, 6), (0, 7), (5, 5)]

diff = [0] * (SEATS + 1)  # one extra slot, so that last + 1 is always a valid index
for first, last in bookings:
    diff[first] += 1
    diff[last + 1] -= 1
    print(f"booking seats {first} to {last}: marks {diff[:SEATS]} (and {diff[SEATS]} past the end)")

count, running = [], 0
for i in range(SEATS):
    running += diff[i]
    count.append(running)
print("prefix sum of the marks:", count)

one_by_one = [0] * SEATS
for first, last in bookings:
    for seat in range(first, last + 1):
        one_by_one[seat] += 1
print("the same, seat by seat: ", one_by_one)

Output

booking seats 1 to 4: marks [0, 1, 0, 0, 0, -1, 0, 0] (and 0 past the end)
booking seats 3 to 6: marks [0, 1, 0, 1, 0, -1, 0, -1] (and 0 past the end)
booking seats 0 to 7: marks [1, 1, 0, 1, 0, -1, 0, -1] (and -1 past the end)
booking seats 5 to 5: marks [1, 1, 0, 1, 0, 0, -1, -1] (and -1 past the end)
prefix sum of the marks: [1, 2, 2, 3, 3, 3, 2, 1]
the same, seat by seat:  [1, 2, 2, 3, 3, 3, 2, 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bookings.py

A booking from first to last adds 1 at diff[first] and subtracts 1 at diff[last + 1], which is why the array has one slot more than there are seats. The running total of the marks rises by 1 where a booking starts and falls by 1 just after it ends, so at every seat it equals the number of bookings covering it, exactly as the seat-by-seat check shows. Recording a booking costs two additions however long it is; reading the result costs one pass.

Prefix sums or difference arrays?

The two are inverses: the running total of a difference array gives the values back, and the differences of neighbouring prefix entries give the original items back. Which one to keep depends on whether the data is mostly asked about or mostly changed.

Prefix sums or a difference array
Criterion Prefix sumsDifference array
Answers the total of any rangethe value of every item, once the updates are in
Setup one pass, O(n)an array of n + 1 zeros
Each update O(n): every later prefix shiftsO(1): two marks per range
Each query O(1): one subtractionO(n) to rebuild the values first
When to choose Many range questions about data that does not changeMany range updates applied before anything is read

Three extensions build on the same idea. On a grid, the total of any rectangle comes from four corner values of a two-dimensional prefix table. Totals need a type wide enough for the largest one: 100,000 values of up to 10⁹ add up to 10¹⁴, beyond any 32-bit integer. When updates and queries arrive mixed together, a Fenwick tree gives both in O(log n).

Exercise

A payments dashboard asks the total of many date ranges over daily figures. The starter code is correct and far too slow; one prefix array fixes it.

Exercise · Easy · Python, JavaScript

Totals of daily payments over many date ranges

A payments dashboard keeps the total received on each day, payments[0] to payments[n - 1] (synthetic figures in whole currency units), and must answer many questions of the form "how much came in from day first to day last, both included?". Write range_totals(payments, queries) (JavaScript: rangeTotals(payments, queries)), where queries is a list of (first, last) pairs (JavaScript: [first, last] arrays) with 0 <= first <= last < n, and return the list of their totals, in the same order.

The starter code adds up every range one by one: it is correct, and far too slow for the last sample test, which asks 200,000 questions about 200,000 days. Daily totals reach 1,000,000,000, so in JavaScript make sure your totals stay exact.

Python · Starter code · range_totals.py

def range_totals(payments, queries):
    """The total of payments[first..last], both included, for each (first, last) in queries."""
    # This adds up every range again: correct, but O(n) per question.
    return [sum(payments[first : last + 1]) for first, last in queries]
The sample tests · test_range_totals.py
import random

from range_totals import range_totals


def test_small():
    """totals of a few ranges of a week, single days included"""
    week = [120, 0, 75, 300, 0, 45, 210]
    assert range_totals(week, [(0, 6), (1, 3), (4, 4), (6, 6), (0, 0)]) == [750, 375, 0, 210, 120]


def test_no_queries():
    """no questions give an empty list"""
    assert range_totals([5, 6], []) == []


def test_large_values():
    """totals beyond 2**31 come out exact"""
    payments = [1_000_000_000] * 5
    assert range_totals(payments, [(0, 4), (2, 3)]) == [5_000_000_000, 2_000_000_000]


def test_many_questions():
    """200,000 questions about 200,000 days finish in time and match a running total"""
    rng = random.Random(2)
    n = 200_000
    payments = [rng.randrange(0, 1_000_000_001) for _ in range(n)]
    queries = []
    for _ in range(200_000):
        first = rng.randrange(n // 4)
        queries.append((first, rng.randrange(n - n // 4, n)))
    totals = range_totals(payments, queries)
    running = [0]
    for p in payments:
        running.append(running[-1] + p)
    assert totals == [running[last + 1] - running[first] for first, last in queries]

JavaScript · Starter code · range_totals.mjs

/** The total of payments[first..last], both included, for each [first, last] in queries. */
export function rangeTotals(payments, queries) {
  // This adds up every range again: correct, but O(n) per question.
  return queries.map(([first, last]) => {
    let total = 0;
    for (let day = first; day <= last; day++) total += payments[day];
    return total;
  });
}
The sample tests · range_totals.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { rangeTotals } from './range_totals.mjs';

test('totals of a few ranges of a week, single days included', () => {
  const week = [120, 0, 75, 300, 0, 45, 210];
  assert.deepEqual(rangeTotals(week, [[0, 6], [1, 3], [4, 4], [6, 6], [0, 0]]), [750, 375, 0, 210, 120]);
});

test('no questions give an empty list', () => assert.deepEqual(rangeTotals([5, 6], []), []));

test('totals beyond 2**31 come out exact', () => {
  assert.deepEqual(rangeTotals([1e9, 1e9, 1e9, 1e9, 1e9], [[0, 4], [2, 3]]), [5e9, 2e9]);
});

test('200,000 questions about 200,000 days finish in time and match a running total', () => {
  let x = 2;
  const next = (bound) => (x = (x * 48271) % 2147483647) % bound;
  const n = 200000;
  const payments = Array.from({ length: n }, () => next(1000000001));
  const queries = Array.from({ length: 200000 }, () => [next(n / 4), n - n / 4 + next(n / 4)]);
  const totals = rangeTotals(payments, queries);
  const running = [0];
  for (const p of payments) running.push(running[running.length - 1] + p);
  assert.deepEqual(totals, queries.map(([first, last]) => running[last + 1] - running[first]));
});
A hint

Build prefix with n + 1 entries first: prefix[0] = 0 and prefix[i + 1] = prefix[i] + payments[i]. Then each query is one subtraction, prefix[last + 1] - prefix[first], so the whole job is O(n + q).

The sample tests run on this device, in your browser (Pyodide, QuickJS): nothing is sent to mysmartcopilot.com. The first run of each language downloads it: Python (about 13.5 MB) or JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 5 a = [4, 1, 3, 6, 2], so prefix = [0, 4, 5, 8, 14, 16]. What is the total of a[1] to a[3], both included?

    Type a number.

    Show the answer to question 1

    Answer: 10

    prefix[4] − prefix[1] = 14 − 4 = 10, which is 1 + 3 + 6. The formula is prefix[last + 1] − prefix[first]: both prefixes contain a[0], which cancels.

  2. Question 2 of 5 Why does the prefix array start with a 0 and have n + 1 entries?

    Choose one answer.

    Show the answer to question 2

    Answer: So that every range, including one that starts at index 0, uses the same formula

    With prefix[0] = 0, a range from 0 to last is prefix[last + 1] − prefix[0], with nothing special. Without it, a range starting at 0 would need prefix[−1], which Python quietly reads as the last entry.

  3. Question 3 of 5 A difference array over seats 0 to 9 records a booking of seats 2 to 5. Which two entries change?

    Choose one answer.

    Show the answer to question 3

    Answer: diff[2] increases by 1 and diff[6] decreases by 1

    The +1 at the first seat starts the booking in the running total, and the −1 at the seat after the last one, seat 6, ends it. Marking diff[5] would stop the booking one seat early.

  4. Question 4 of 5 A year of daily sales never changes, and the dashboard asks a million range totals a day. Which structure fits?

    Choose one answer.

    Show the answer to question 4

    Answer: A prefix array, built once

    The data is fixed and the queries are many, which is exactly what a prefix array is for: O(n) once, then O(1) per query. A difference array is for the opposite job, many range updates applied before the values are read.

  5. Question 5 of 5 100,000 bookings, each covering 1,000 seats of a 100,000-seat hall, are applied with a difference array. About how many additions does that take, in thousands? (Two per booking, then one per seat.)

    Type a number.

    Show the answer to question 5

    Answer: 300

    2 × 100,000 marks plus 100,000 for the running total: 300,000 additions. Applying the same bookings one seat at a time would take 100,000 × 1,000 = 100 million.

Grids: two-dimensional prefix sums and difference grids, worked Pro: opens with a Pro, Premium or Ultimate pass

On a grid, a prefix table answers the total of any rectangle with four lookups, and a difference grid applies a rectangle update with four marks. This part works both on a 4 × 5 grid, checks them against every rectangle, and reproduces the off-by-one that the border of zeros prevents.

In this part:

  • Inclusion and exclusion on a 4 × 5 grid, worked
  • The off-by-one that the border prevents, reproduced
  • A difference grid: four marks per rectangle

1 runnable example (Python) · about 2 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

64-bit totals, five languages and measured speed Pro: opens with a Pro, Premium or Ultimate pass

Prefix totals grow with the data, and a type that holds every item can still overflow on the totals. This part sizes the totals, reproduces the overflow in Java, C++ and Go and the loss of precision in JavaScript, and measures what a difference array saves on 100,000 bookings.

In this part:

  • How large the totals get, with numbers
  • Prefix sums in four more languages, with the overflow reproduced
  • A difference array against one seat at a time, measured

3 runnable examples (C++, Go, Java, JavaScript, Python) · about 3 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Beyond sums and fixed data: counts, XOR and changing arrays Pro: opens with a Pro, Premium or Ultimate pass

A prefix table works for any operation that can be undone, and it stops working when the data changes between questions. This part shows which operations qualify, why maximum does not, and how to choose between patching, rebuilding and a tree, with break-even numbers.

In this part:

  • Counts and XOR work; maximum does not
  • Negative numbers: why windows fail and running totals do not
  • When updates and queries mix, with break-even numbers

1 runnable example (Python) · about 2 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Interview questions

  1. Fresher · Warm-up

    How does a difference array apply many range increments at once?

    It records each update by its two ends instead of touching every item. To add v to items first to last, add v at diff[first] and subtract v at diff[last + 1], which is why the array has one slot more than the data. After all the updates are recorded, a single running total over diff gives every item's final value: the total rises by v where a range starts and falls back just after it ends, so each item receives exactly the updates that cover it. Recording costs O(1) per update and the final pass O(n), so q updates cost O(n + q) instead of up to n × q. It fits updates that all arrive before the values are read.

  2. Fresher to mid

    A booking system holds 200,000 bookings, each reserving a range of seats on a coach of 100,000 seats, and must report how many bookings cover each seat. A loop that adds 1 to every seat of every booking takes too long. Explain the cost with numbers and fix it.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A dashboard must return the total of any rectangle of a 2,000 × 2,000 grid of daily sales figures, each up to 1,000,000, about a million times a day, using at most 64 MB. Choose the structure, its element type and its memory with numbers.

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    Range increments now arrive interleaved with point queries, about 100,000 of each, on 1,000,000 positions: a query asks for one position's current value. Why does a difference array stop working, and what would you use instead? Compare the costs.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Prefix sums turn range totals into one subtraction: prefix[last + 1] − prefix[first], after O(n) work once.
  • Keep prefix[0] = 0 and think in half-open ranges; the off-by-one slips disappear.
  • Difference arrays record a range update as two marks; one running total applies them all, O(n + q).
  • Prefix sums suit fixed data with many questions; difference arrays suit many updates read once at the end.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.