Difference Array
Apply many range increments in O(1) each by writing +v at l and −v at r+1, then recover the final array with a single prefix-sum pass.
Overview
A difference array is the inverse of a Prefix Sum. Where prefix sums make range queries cheap on a static array, difference arrays make range updates cheap when all updates are known before any query — an *offline* setting. Store D[i] = a[i] − a[i-1]. To add v to every element in a[l..r], set D[l] += v and D[r+1] −= v: two O(1) writes regardless of the range length. After all updates, one prefix-sum pass over D reconstructs a.
This solves "m flight bookings over n flights", "count overlapping intervals at each point", "car pooling capacity", and any problem framed as "for each of m operations, add v to a range; return the final array" in O(n + m) instead of O(n·m).
Intuition
A mental model before the formal terms.
Imagine painting a fence with many overlapping strokes and then asking how many coats each plank has. Rather than touching every plank in every stroke, put a sticky note at the first plank saying "+1 from here on" and another just past the last plank saying "−1 from here on". Afterwards, walk the fence once left to right, keeping a running tally of the notes you have passed. The tally at each plank is its coat count.
How it works
- Allocate
Dof lengthn + 1filled with zeros (the extra slot absorbsr + 1 = n). - For each update
(l, r, v):D[l] += v,D[r+1] −= v. - After all updates, run a prefix sum:
a[0] = D[0],a[i] = a[i-1] + D[i]fori ≥ 1. - If the array had initial values
a₀, either startDas the difference ofa₀or adda₀[i]back at the end. - For 2D ranges (add
vto a submatrix), place four corner marks+v, −v, −v, +vand run 2D Prefix Sum to reconstruct.
Why it works
Prefix-sum of `D` reproduces `a`: define a[i] = D[0] + … + D[i]. A single update writes +v at l and −v at r+1. For i < l neither mark is included, so a[i] is unchanged. For l ≤ i ≤ r only the +v is included, so a[i] rises by exactly v. For i > r both marks are included and cancel, so a[i] is unchanged. That is precisely "add v to [l, r]".
Updates commute: prefix sum is linear, so the effect of many updates on D is the sum of their individual effects on a. Order of application is irrelevant, which is why the updates can be recorded lazily and resolved in one pass.
Cost: O(1) per update and O(n) for the final pass, so O(n + m) for m updates — versus O(m · avg range length) for direct application.
Recognition
How to tell a problem wants this.
- "Add `v` to every element in `[l, r]`", "increment a range", "apply
moperations then return the array", "bookings", "reservations". - "How many intervals cover each point", "maximum number of overlapping meetings/passengers at any moment", "car pooling" — each interval is a
+1at start and−1at end. - The updates are all given up front (offline) and queries come only after them — if updates and queries interleave, you need a Fenwick Tree with range-update support or a Segment Tree with lazy propagation.
- Constraints
n, m ≤ 10^5where naive range application would be10^10. - Coordinates that are sparse or huge (
10^9): use a sorted map of breakpoints (sweep line) instead of a dense array — same idea, compressed.
Interactive visualization
Play, step, change the input. ← → and space work too.
1D = [0] * (n + 1)2for each update (l, r, v):3 D[l] += v; D[r+1] -= v4a[0] = D[0]; for i in 1..n-1: a[i] = a[i-1] + D[i]Pseudocode
1D = array of n + 1 zeros2for (l, r, v) in updates:3 D[l] += v4 D[r + 1] -= v5a[0] = D[0]6for i in 1..n-1: a[i] = a[i-1] + D[i]7return aImplementations
1# Corporate Flight Bookings: bookings[j] = [first, last, seats] (1-based inclusive)2def corp_flight_bookings(bookings: list[list[int]], n: int) -> list[int]:31 · Difference array with one extra slot so r + 1 == n stays in bounds4 d = [0] * (n + 1)52 · Each booking becomes two O(1) marks: +seats at l, -seats just past r6 for first, last, seats in bookings:7 d[first - 1] += seats # convert 1-based first to 0-based l8 d[last] -= seats # (last - 1) + 1 == last93 · One prefix-sum pass turns the marks into final seat counts10 out = [0] * n11 run = 012 for i in range(n):13 run += d[i]14 out[i] = run15 return out[0] * (n + 1)allocates the sentinel slot; Python lists are zero-filled here by construction.- Tuple unpacking
for first, last, seats in bookingsnames the fields directly in the loop header. - The two marks are O(1) list writes; negative indices would silently wrap in Python, so the 1-based conversion deserves its comment.
- The reconstruction accumulates
runin a plain loop — clear, and O(1) extra beyond the output. - Python ints cannot overflow, so no width analysis is needed for
run.
itertools.accumulate can replace the final loop; slicing d[:n] first copies O(n) — see the alternative.
itertools.accumulateis the stdlib prefix-sum;list(accumulate(d[:n]))is the one-liner reconstruction (the slice makes an O(n) copy — fine here, but a real cost in tight loops).- Beware negative indices:
d[first - 1]with a badfirst = 0input would write tod[-1](the last slot) instead of raising. - For sparse/huge coordinates use a dict of breakpoints plus
sorted(marks)— the sweep-line form of the same idea.
- Writing
d = [0] * nand getting anIndexError(or, with negative indices, silent corruption) atd[last]whenlast == n. - Returning the prefix sums of the whole
dincluding the sentinel — the answer has n entries, not n + 1. - Applying each booking with a
for i in range(first - 1, last)loop — O(n·m), the exact thing the technique avoids.
- Zero initialisation: C++
vector<int>(n+1, 0)and Python[0] * (n+1)are zeroed by construction; JS/TSnew Array(n+1)has holes until.fill(0)—Int32Arrayis the zero-by-contract alternative. - Out-of-range writes: C++
d[n+1]is undefined behaviour (silent corruption); Python negative indices wrap around instead of failing; JS/TS writing past the end silently grows the array — three different failure modes for the same off-by-one. - Overflow of accumulated totals: C++ needs
long longwhenm * max(v)can pass 2^31; JS/TS are exact to 2^53; Python is unbounded. - Stdlib reconstruction: Python
itertools.accumulate(slicing first copies O(n)); C++std::partial_sum(accumulates in the input value_type — a widening trap); JS/TS have no scan primitive, so the manual loop is idiomatic.
Complexity
m range updates at O(1) each plus one O(n) reconstruction. Queries are only valid after reconstruction (offline).
Compare growth rates in the Complexity Explorer →When to use — and when not to
- Many range increments, all known before the final values are needed.
- Counting coverage / overlaps of intervals over integer positions.
- Batch application of operations to an array (bookings, salary raises, sensor calibrations).
- 2D variant: submatrix increments resolved with a 2D prefix sum.
- Updates and queries interleave ("add to range, then query a point, then add again") — reconstruction after each update is
O(n); use a Fenwick Tree (range update, point query) or lazy Segment Tree. - Updates are not additive (set every element to
v, multiply, take max) — a plain difference array only encodes additions; use a segment tree with the right lazy tag. - Only a handful of updates over a short array — direct loops are fine and clearer.
- Coordinates are huge and sparse — use a sorted breakpoint map (sweep line) rather than allocating
10^9slots.
Alternatives
Common mistakes
- Allocating
Dof lengthnand writingD[r+1]out of bounds whenr = n − 1; always allocaten + 1. - Placing the
−vatrinstead ofr + 1, which excludesa[r]from the update. - Mixing 1-based problem input with 0-based indices — convert once at the boundary and comment it.
- Querying values before running the reconstruction pass.
- Forgetting to include the original array's initial values in the result.
Interview patterns
- Corporate Flight Bookings — the textbook difference array.
- Car Pooling:
+passengersat pickup,−passengersat drop-off; check running total ≤ capacity. - Meeting Rooms II via sweep of
+1/−1events (sort events when times are not small integers). - Range Addition: apply
kupdates to a zero array. - Number of Flowers in Full Bloom / Points covered by intervals — coverage counts.
- 2D Range Addition: four corner marks per rectangle plus 2D Prefix Sum.
- Recognizing the approach from an array and a targetIntermediate
- Deciding whether O(n²) can be improvedIntermediate
- Recognizing a sliding-window problemIntermediate
- Prefix sum or segment tree?Intermediate
- Minimum Size Subarray SumIntermediate
- Merge IntervalsIntermediate
- Subarray Sum Equals KIntermediate