Difference ArrayRange UpdatesPrefix SumSweep LineLazy Propagation

Difference Array — The Complete Guide

Master the difference array from first principles. Learn to turn O(n) range updates into O(1), recognize the pattern instantly, and confidently solve every range-update interview question.

40 min read10 sections
01

Intuition from Scratch

Why does this pattern exist?

Imagine you have an array of 1 million zeros and you receive 100,000 operations, each saying "add 5 to every element from index 200 to index 8000." The brute-force approach updates every element in the range for each operation — that's 100,000 × 7,800 = 780 million operations. Way too slow.

The difference array is a trick that turns each range update from O(n) into O(1). Instead of updating every element in the range, you only mark the start and end of the range. After all operations are done, you reconstruct the final array with a single prefix sum pass. Total cost: O(q + n) instead of O(q × n), where q is the number of operations.

The core idea is beautifully simple: if you want to add a value to a range [l, r], you add it at position l and subtract it at position r+1. When you later compute the prefix sum, the addition "starts" at l and the subtraction "cancels" it after r. The effect is exactly the same as updating every element in the range — but in O(1) per operation.

How It Works (The Basics)

Difference Array — Core Ideatext
Goal: Add val to every element in range [l, r]

Instead of:  for (i = l; i <= r; i++) arr[i] += val;  // O(n) per update

Do this:     diff[l] += val;       // start the effect
             diff[r + 1] -= val;   // cancel the effect after r

After all updates: arr = prefixSum(diff)  // reconstruct in O(n)

Example: array of size 5, add 3 to range [1, 3]

diff = [0, 0, 0, 0, 0]
diff[1] += 3  →  [0, 3, 0, 0, 0]
diff[4] -= 3  →  [0, 3, 0, 0, -3]

Prefix sum:  [0, 3, 3, 3, 0]  ← exactly what we wanted!

The +3 at index 1 "carries forward" through the prefix sum.
The -3 at index 4 "cancels" it, so indices 4+ are unaffected.

Analogies to Build Intuition

🚌

Bus Passengers

Imagine tracking passengers on a bus route with 10 stops. Instead of counting everyone on the bus at each stop, you just record '+3 at stop 2' (3 people board) and '-3 at stop 6' (3 people exit). At the end, a running total tells you exactly how many people were on the bus at each stop. That's a difference array — record the changes, compute the totals later.

🌡️

Thermostat Schedule

You want to raise the temperature by 5°F from 9am to 5pm. Instead of setting every hour individually, you set '+5 at 9am' and '-5 at 5pm'. The heating system accumulates these changes over time. That's the difference array — mark where changes start and stop, let the prefix sum handle the rest.

🎨

Painting a Fence

You have a long fence and multiple paint orders: 'paint sections 3-7 red', 'paint sections 5-10 blue'. Instead of painting each plank individually for each order, you put a 'start painting' marker at the beginning and a 'stop painting' marker at the end. One sweep along the fence applies all the paint. That's batch processing with a difference array.

Relationship to Prefix Sum

Difference array and prefix sum are inverses of each other. If prefix sum answers "what's the sum of a range?" in O(1) after O(n) preprocessing, then difference array answers "update a range" in O(1) with O(n) reconstruction. They are two sides of the same coin:

  • Prefix Sum: many range queries, few updates → precompute prefix sums
  • Difference Array: many range updates, few queries → use difference array + final prefix sum

The core insight

A difference array records where changes start and stop. A prefix sum over the difference array reconstructs the actual values. This turns O(n) range updates into O(1) — the most important optimization for batch range-update problems.

02

Pattern Recognition

Recognizing when a difference array is the right tool is the most important skill. Here are the signals to look for and the traps to avoid.

Keywords & Signals in the Problem Statement

Use Difference Array when you see...

  • "Add val to all elements in range [l, r]" — repeated range updates
  • "Increment/decrement a subarray" — batch modifications
  • "Number of overlapping intervals at each point"
  • "Car pooling" — passengers boarding and exiting at stops
  • "Flight bookings" — seats reserved across ranges
  • "After all operations, return the final array"
  • "Maximum number of events/meetings at any time" (sweep line variant)
  • Multiple range updates followed by a single query for the final state
  • Constraints: large array (10⁵+) with many operations (10⁵+)

Don't use Difference Array when...

  • You need to query range sums between updates — use a Segment Tree or BIT
  • Updates are point updates (single index) — just update directly
  • You need the result after EACH update, not just at the end — use a Segment Tree
  • The problem is about range queries, not range updates — use Prefix Sum
  • The array is tiny — brute force is fine and simpler
  • Updates are multiplicative (multiply range by val) — difference array only works for additive updates

Difference Array vs. Similar Patterns

PatternWhen to UseKey Difference
Difference ArrayMany range updates, query final state onceO(1) per update, O(n) to reconstruct — batch processing
Prefix SumMany range queries, no updates (or very few)O(1) per query after O(n) preprocessing — the inverse of difference array
Segment TreeInterleaved range updates AND range queriesO(log n) per update and query — more powerful but more complex
Binary Indexed Tree (BIT)Point updates + prefix queriesO(log n) per operation — simpler than segment tree for point updates
Sweep LineEvent-based problems (intervals, meetings, overlaps)Sort events by time, process left-to-right — difference array is the discrete version

The difference array question to always ask

When you see a problem with multiple range updates, ask: "Do I need the result after each update, or only at the end?" If only at the end → difference array (O(1) per update). If after each update → segment tree (O(log n) per update). This single question determines your approach.

03

Brute Force → Optimized Thinking

Let's walk through the thinking evolution using a concrete example: Corporate Flight Bookings — there are n flights and a list of bookings where each booking reserves seats on flights from first_i to last_i. Return the total seats reserved on each flight.

1

Brute Force — Update Every Flight in Each Booking

O(n × q) time · O(n) space

For each booking [first, last, seats], iterate from first to last and add seats to each flight. With 20,000 flights and 20,000 bookings, this is 400 million operations — TLE.

Brute Forcetypescript
function corpFlightBookingsBrute(
  bookings: number[][],
  n: number
): number[] {
  const seats = new Array(n).fill(0);

  for (const [first, last, count] of bookings) {
    for (let i = first - 1; i < last; i++) {
      seats[i] += count; // O(n) per booking
    }
  }

  return seats;
}
// Problem: Each booking updates up to n flights.
// With q bookings: O(n × q) total. Way too slow for large inputs.
2

Optimal — Difference Array

O(n + q) time · O(n) space

Instead of updating every flight in the range, mark the start and end of each booking in a difference array. Then compute the prefix sum to get the final seat counts. Each booking is O(1) instead of O(n).

Difference Array — Optimaltypescript
function corpFlightBookings(
  bookings: number[][],
  n: number
): number[] {
  const diff = new Array(n + 1).fill(0); // +1 for the r+1 cancellation

  for (const [first, last, seats] of bookings) {
    diff[first - 1] += seats;  // start the effect (1-indexed → 0-indexed)
    diff[last] -= seats;       // cancel after last (last is 1-indexed, so diff[last] is r+1 in 0-indexed)
  }

  // Prefix sum to reconstruct
  const result = new Array(n);
  result[0] = diff[0];
  for (let i = 1; i < n; i++) {
    result[i] = result[i - 1] + diff[i];
  }

  return result;
}
// Each booking: O(1). Prefix sum: O(n). Total: O(n + q).
// 20,000 flights + 20,000 bookings = 40,000 operations. Done.

Why Does the Optimization Work?

1

Mark boundaries instead of filling ranges

Adding val at index l and subtracting val at index r+1 is like saying 'the effect starts here and stops there.' You don't need to touch every element in between — the prefix sum will propagate the effect automatically.

2

Prefix sum reconstructs the actual values

The prefix sum of the difference array accumulates all the +val and -val markers. At any index i, the running sum equals the total of all range updates that include index i. It's like replaying all the operations in one linear pass.

3

Multiple overlapping ranges compose correctly

If booking A adds 10 to [2, 5] and booking B adds 20 to [3, 7], the difference array has +10 at 2, +20 at 3, -10 at 6, -20 at 8. The prefix sum correctly gives 10 at index 2, 30 at indices 3-5, 20 at indices 6-7, and 0 after. Overlapping ranges just add up.

The thinking process matters more than the code

In interviews, walk through this progression: "Brute force updates every element in each range — O(n × q). But I notice that each range update has a clear start and end. If I just mark the boundaries and compute a prefix sum at the end, each update is O(1) and reconstruction is O(n). Total: O(n + q)."

04

Core Templates

Difference array problems follow a few recurring templates. Memorize these structures — most problems are variations of one of them.

Template 1: Basic Difference Array (Range Add)

The fundamental template. Apply multiple "add val to range [l, r]" operations, then reconstruct the final array.

Basic Difference Array Templatetypescript
function rangeAdd(n: number, updates: [number, number, number][]): number[] {
  // 1. Create difference array (size n+1 for safe r+1 access)
  const diff = new Array(n + 1).fill(0);

  // 2. Apply each update in O(1)
  for (const [l, r, val] of updates) {
    diff[l] += val;      // effect starts at l
    diff[r + 1] -= val;  // effect cancels after r
  }

  // 3. Prefix sum to reconstruct the actual array
  const result = new Array(n);
  result[0] = diff[0];
  for (let i = 1; i < n; i++) {
    result[i] = result[i - 1] + diff[i];
  }

  return result;
}
// Time: O(n + q) where q = number of updates
// Space: O(n)
// When to use: Range Update Array, Corporate Flight Bookings,
// any "add val to range [l, r]" batch problem

Template 2: Difference Array on Existing Array

When the array starts with non-zero values, first convert it to a difference array, apply updates, then reconstruct.

Difference Array on Existing Arraytypescript
function rangeAddOnExisting(
  arr: number[],
  updates: [number, number, number][]
): number[] {
  const n = arr.length;

  // 1. Convert original array to difference array
  const diff = new Array(n + 1).fill(0);
  diff[0] = arr[0];
  for (let i = 1; i < n; i++) {
    diff[i] = arr[i] - arr[i - 1];
  }

  // 2. Apply updates in O(1) each
  for (const [l, r, val] of updates) {
    diff[l] += val;
    diff[r + 1] -= val;
  }

  // 3. Prefix sum to reconstruct
  const result = new Array(n);
  result[0] = diff[0];
  for (let i = 1; i < n; i++) {
    result[i] = result[i - 1] + diff[i];
  }

  return result;
}
// Key: diff[i] = arr[i] - arr[i-1] converts any array to its difference form.
// Prefix sum of the difference array gives back the original array.

Template 3: Sweep Line (Event-Based Difference Array)

When the "array" represents time or positions and you have intervals (events), use a difference map or array to count overlaps. This is the sweep line technique — the continuous version of a difference array.

Sweep Line Templatetypescript
function maxOverlap(intervals: [number, number][]): number {
  // Use a map when the range is sparse or values are large
  const events = new Map<number, number>();

  for (const [start, end] of intervals) {
    events.set(start, (events.get(start) ?? 0) + 1);  // interval starts
    events.set(end, (events.get(end) ?? 0) - 1);      // interval ends
  }

  // Sort events by time and sweep
  const sorted = [...events.entries()].sort((a, b) => a[0] - b[0]);

  let current = 0;
  let maxConcurrent = 0;

  for (const [, delta] of sorted) {
    current += delta;
    maxConcurrent = Math.max(maxConcurrent, current);
  }

  return maxConcurrent;
}
// When to use: maximum overlapping intervals, meeting rooms,
// car pooling, minimum platforms at a station
// Key: +1 at start, -1 at end, then sweep with running sum.

Template 4: 2D Difference Array

Extend the idea to 2D grids. To add val to a rectangle (r1, c1) → (r2, c2), mark four corners. Then compute a 2D prefix sum to reconstruct.

2D Difference Array Templatetypescript
function rangeAdd2D(
  rows: number, cols: number,
  updates: [number, number, number, number, number][] // [r1, c1, r2, c2, val]
): number[][] {
  // 1. Create 2D difference array (extra row and column)
  const diff = Array.from({ length: rows + 1 }, () => new Array(cols + 1).fill(0));

  // 2. Apply each update in O(1)
  for (const [r1, c1, r2, c2, val] of updates) {
    diff[r1][c1] += val;
    diff[r1][c2 + 1] -= val;
    diff[r2 + 1][c1] -= val;
    diff[r2 + 1][c2 + 1] += val; // inclusion-exclusion correction
  }

  // 3. 2D prefix sum to reconstruct
  for (let r = 0; r < rows; r++) {
    for (let c = 1; c < cols; c++) {
      diff[r][c] += diff[r][c - 1]; // row prefix sum
    }
  }
  for (let c = 0; c < cols; c++) {
    for (let r = 1; r < rows; r++) {
      diff[r][c] += diff[r - 1][c]; // column prefix sum
    }
  }

  return diff.slice(0, rows).map(row => row.slice(0, cols));
}
// Time: O(rows × cols + q)
// When to use: stamp grid, 2D range updates, matrix block operations

Which template to pick?

Ask yourself: (1) Simple range add on a 1D array? → Template 1. (2) Array already has values? → Template 2. (3) Counting overlapping intervals or events? → Template 3. (4) Range updates on a 2D grid? → Template 4.

05

Variations & Sub-Patterns

The difference array isn't a single trick — it's a family of techniques that all share the "mark boundaries, sweep later" idea.

VariationStrategyClassic Example
Basic Range Adddiff[l] += val, diff[r+1] -= val, prefix sumRange Addition, Corporate Flight Bookings
Sweep Line (Intervals)+1 at start, -1 at end, sort events, running sumMeeting Rooms II, Car Pooling, Minimum Platforms
Boolean Range Togglediff[l] ^= 1, diff[r+1] ^= 1, prefix XORBulb Switcher III, Range Toggle problems
2D Difference ArrayMark 4 corners of rectangle, 2D prefix sumStamping the Grid, 2D Range Update
Difference + Binary SearchApply difference array, then binary search on prefix sum for thresholdMinimum Number of Operations to Make Array Continuous
Difference + GreedyUse difference array to track remaining capacity, greedily assignCar Pooling, My Calendar III

Problems mentioned above

Deep Dive: Car Pooling — Difference Array + Capacity Check

A car has a capacity limit. Passengers board at pickup and exit at dropoff. Determine if the car can complete all trips without exceeding capacity. This is a difference array problem where you check if the running sum ever exceeds the limit.

Car Poolingtypescript
function carPooling(trips: number[][], capacity: number): boolean {
  // Locations range from 0 to 1000
  const diff = new Array(1001).fill(0);

  for (const [passengers, from, to] of trips) {
    diff[from] += passengers;  // board at 'from'
    diff[to] -= passengers;    // exit at 'to' (not 'to + 1' — they exit BEFORE 'to')
  }

  // Sweep: check if capacity is ever exceeded
  let current = 0;
  for (let i = 0; i < diff.length; i++) {
    current += diff[i];
    if (current > capacity) return false;
  }

  return true;
}
// Time: O(n + 1001) where n = number of trips
// Space: O(1001) = O(1) since location range is bounded
// Key: passengers exit BEFORE the 'to' location, so diff[to] -= passengers
// (not diff[to + 1]). Read the problem carefully for inclusive/exclusive bounds.

Deep Dive: Sweep Line — Meeting Rooms II

Given meeting intervals, find the minimum number of conference rooms required. This is the sweep line variant: +1 when a meeting starts, -1 when it ends, and the peak of the running sum is the answer.

Meeting Rooms II — Sweep Linetypescript
function minMeetingRooms(intervals: number[][]): number {
  const events: [number, number][] = [];

  for (const [start, end] of intervals) {
    events.push([start, 1]);   // meeting starts → +1 room
    events.push([end, -1]);    // meeting ends → -1 room
  }

  // Sort by time. If same time, process ends (-1) before starts (+1)
  // so a room freed at time t can be reused by a meeting starting at t
  events.sort((a, b) => a[0] - b[0] || a[1] - b[1]);

  let rooms = 0;
  let maxRooms = 0;

  for (const [, delta] of events) {
    rooms += delta;
    maxRooms = Math.max(maxRooms, rooms);
  }

  return maxRooms;
}
// Time: O(n log n) for sorting events
// Space: O(n) for events array
// Key: this is a difference array on a timeline.
// +1 at start, -1 at end, sweep for the peak.
// Sorting tie-break: ends before starts at the same time.

Deep Dive: 2D Difference Array — Stamping the Grid

When you need to apply rectangular updates to a 2D grid, the 2D difference array uses inclusion-exclusion at four corners. This extends the 1D idea to two dimensions.

2D Difference Array — Intuitiontext
To add val to rectangle (r1,c1) → (r2,c2):

  diff[r1][c1]       += val   (top-left: start effect)
  diff[r1][c2+1]     -= val   (top-right+1: cancel rightward)
  diff[r2+1][c1]     -= val   (bottom+1-left: cancel downward)
  diff[r2+1][c2+1]   += val   (bottom+1-right+1: fix double subtraction)

This is inclusion-exclusion in 2D:
  ┌─────────┐
  │ +val    │-val
  │         │
  └─────────┘
  -val      +val  (correction)

After all updates, compute 2D prefix sum:
  1. Row-wise prefix sum (left to right)
  2. Column-wise prefix sum (top to bottom)

Result: each cell has the correct accumulated value.
06

Visual Walkthrough

Let's trace through three problems step by step to see the difference array in action.

Range Addition — Step by Step

Range Addition — Tracetext
n = 5, updates = [[1,3,2], [2,4,3], [0,2,-2]]

Start: diff = [0, 0, 0, 0, 0, 0]  (size n+1 = 6)

Update 1: add 2 to range [1, 3]
  diff[1] += 2, diff[4] -= 2
  diff = [0, 2, 0, 0, -2, 0]

Update 2: add 3 to range [2, 4]
  diff[2] += 3, diff[5] -= 3
  diff = [0, 2, 3, 0, -2, -3]

Update 3: add -2 to range [0, 2]
  diff[0] += -2, diff[3] -= -2
  diff = [-2, 2, 3, 2, -2, -3]

Prefix sum to reconstruct:
  i=0: result[0] = -2
  i=1: result[1] = -2 + 2 = 0
  i=2: result[2] = 0 + 3 = 3
  i=3: result[3] = 3 + 2 = 5
  i=4: result[4] = 5 + (-2) = 3

Answer: [-2, 0, 3, 5, 3]

Verification (brute force):
  Start:    [0, 0, 0, 0, 0]
  +2 [1,3]: [0, 2, 2, 2, 0]
  +3 [2,4]: [0, 2, 5, 5, 3]
  -2 [0,2]: [-2, 0, 3, 5, 3]  ✓ Matches!

Car Pooling — Step by Step

Car Pooling — Tracetext
trips = [[2,1,5], [3,3,7]], capacity = 4

diff array (locations 0-7):
  diff = [0, 0, 0, 0, 0, 0, 0, 0]

Trip 1: 2 passengers, from=1, to=5
  diff[1] += 2, diff[5] -= 2
  diff = [0, 2, 0, 0, 0, -2, 0, 0]

Trip 2: 3 passengers, from=3, to=7
  diff[3] += 3, diff[7] -= 3
  diff = [0, 2, 0, 3, 0, -2, 0, -3]

Sweep (running sum):
  loc 0: current = 04
  loc 1: current = 24
  loc 2: current = 24
  loc 3: current = 5  > 4 ✗ → EXCEEDS CAPACITY!

Answer: false

At location 3, both trips overlap: 2 + 3 = 5 passengers > capacity 4.

Key: diff[to] -= passengers (not diff[to+1]) because passengers
exit BEFORE reaching 'to'. Always check inclusive/exclusive bounds.

Corporate Flight Bookings — Step by Step

Corporate Flight Bookings — Tracetext
n = 5, bookings = [[1,2,10], [2,3,20], [2,5,25]]
(flights are 1-indexed)

diff = [0, 0, 0, 0, 0, 0]  (size n+1 = 6, 0-indexed)

Booking 1: 10 seats on flights 1-2
  diff[0] += 10, diff[2] -= 10    (1-indexed0-indexed: l=0, r+1=2)
  diff = [10, 0, -10, 0, 0, 0]

Booking 2: 20 seats on flights 2-3
  diff[1] += 20, diff[3] -= 20
  diff = [10, 20, -10, -20, 0, 0]

Booking 3: 25 seats on flights 2-5
  diff[1] += 25, diff[5] -= 25
  diff = [10, 45, -10, -20, 0, -25]

Prefix sum:
  flight 1: 10
  flight 2: 10 + 45 = 55
  flight 3: 55 + (-10) = 45
  flight 4: 45 + (-20) = 25
  flight 5: 25 + 0 = 25

Answer: [10, 55, 45, 25, 25]

Verification:
  Flight 1: booking 1 = 1010
  Flight 2: booking 1 + 2 + 3 = 10+20+2555
  Flight 3: booking 2 + 3 = 20+2545
  Flight 4: booking 3 = 2525
  Flight 5: booking 3 = 2525
07

Practice Problems

These 7 problems are carefully ordered from easy to hard. Each one teaches a different aspect of the difference array pattern. Solve them in order.

1

LC 370. Range Addition

Medium

Why this pattern:

The foundational difference array problem. Given an array of zeros and a list of [start, end, val] updates, return the final array. This is Template 1 in its purest form.

Key idea: Create diff array. For each update: diff[l] += val, diff[r+1] -= val. Prefix sum to reconstruct. O(n + q) total. This is the 'hello world' of difference arrays.

2

LC 1109. Corporate Flight Bookings

Medium

Why this pattern:

Difference array with 1-indexed input. Each booking reserves seats on a range of flights. Convert 1-indexed to 0-indexed, apply difference array, prefix sum to get total seats per flight.

Key idea: Same as Range Addition but with 1-indexed flights. diff[first-1] += seats, diff[last] -= seats. The main trap is the index conversion — draw it out to avoid off-by-one errors.

3

LC 1094. Car Pooling

Medium

Why this pattern:

Difference array + capacity check. Passengers board at 'from' and exit at 'to'. Apply difference array, then sweep to check if the running sum ever exceeds capacity.

Key idea: diff[from] += passengers, diff[to] -= passengers (passengers exit BEFORE 'to', so no +1). Sweep with running sum. If it ever exceeds capacity, return false. The bound is exclusive on the right — read carefully.

4

LC 1551. Minimum Number of Operations to Make Array Equal

Medium

Why this pattern:

Mathematical insight that connects to difference arrays conceptually. The array is [1, 3, 5, ..., 2n-1] and you want to make all elements equal. The target is n (the median). Count the total difference.

Key idea: Target = n. Sum of differences from the left half: sum of (n - (2i+1)) for i = 0 to n/2-1. This simplifies to n²/4 (or (n²-1)/4 for odd n). The connection to difference arrays: each operation is a range update that brings two elements closer to the median.

5

LC 2381. Shifting Letters II

Medium

Why this pattern:

Difference array on character shifts. Each shift operation adds +1 or -1 to a range of characters. Apply difference array to compute the net shift at each position, then apply to the string.

Key idea: For each shift [l, r, direction]: if forward, diff[l] += 1, diff[r+1] -= 1. If backward, diff[l] -= 1, diff[r+1] += 1. Prefix sum gives net shift per character. Apply modulo 26 to wrap around the alphabet.

6

LC 1943. Describe the Painting

Medium

Why this pattern:

Sweep line with difference array on a coordinate line. Paint segments with colors (represented as sums). Use a difference map to mark where color sums change, then sweep to find contiguous segments with the same total color.

Key idea: For each segment [l, r, color]: events[l] += color, events[r] -= color. Sort events by position. Sweep left-to-right: whenever the running sum changes, start a new segment. Output segments with their color sums.

7

LC 732. My Calendar III

Hard

Why this pattern:

Online sweep line with a sorted map. Each booking adds +1 at start and -1 at end. After each booking, sweep the entire map to find the maximum overlap (K-booking). This is the dynamic/online version of the difference array.

Key idea: Use a TreeMap (sorted map): map[start] += 1, map[end] -= 1. After each booking, sweep all entries to find the peak running sum. O(n) per booking, O(n²) total. For better performance, use a segment tree — but the sweep line approach is the expected interview solution.

Practice strategy

For each problem: (1) Identify the range update operation. (2) Ask "what do I add at the start and subtract at the end?" (3) Check if the right bound is inclusive or exclusive. (4) After solving, write: "diff[l] += X, diff[r+1] -= X because [reason]."

08

Common Mistakes

These are the traps that catch people on difference array problems. Learn them here so you don't learn them in an interview.

📏

Off-by-one on the right boundary

You subtract at index r instead of r+1 (or vice versa). This either cuts the effect one position short or extends it one position too far. The most common difference array bug.

Always draw it out: 'I want the effect to include index r. So I cancel at r+1.' For problems like Car Pooling where passengers exit BEFORE the destination, the cancellation is at r (not r+1). Read the problem statement carefully for inclusive vs exclusive bounds.

🔢

Array too small — out-of-bounds on diff[r+1]

You create diff with size n, but diff[r+1] can be index n when r = n-1. This causes an array index out of bounds error.

Always create the difference array with size n+1 (or n+2 to be safe). The extra slot absorbs the r+1 cancellation for the last valid index. This is the #1 implementation bug.

🔄

Forgetting to convert 1-indexed to 0-indexed

The problem uses 1-indexed ranges (like Corporate Flight Bookings) but you apply them directly to a 0-indexed array. Everything shifts by one and the answer is wrong.

When the problem says 'flights 1 to n', convert: diff[first - 1] += val, diff[last] -= val (since last in 1-indexed = last-1+1 = last in 0-indexed for the cancellation). Always clarify indexing before coding.

⏱️

Using difference array when you need intermediate results

You need the array state after EACH update (not just at the end), but you use a difference array which only gives the final state. You'd need to recompute the prefix sum after every update — O(n) per query, defeating the purpose.

If you need the state after each update, use a Segment Tree with lazy propagation (O(log n) per update and query). Difference arrays are for batch processing — all updates first, one reconstruction at the end.

🎯

Wrong tie-breaking in sweep line

In Meeting Rooms II, a meeting ending at time 5 and another starting at time 5 should share the room. But if you process starts before ends at the same time, you count an extra room.

When events have the same time, process ends (-1) before starts (+1). Sort by (time, delta) so -1 comes before +1 at the same timestamp. This ensures rooms are freed before new meetings claim them.

📐

Forgetting the inclusion-exclusion correction in 2D

In 2D difference arrays, you mark three corners but forget the fourth (bottom-right correction). The 2D prefix sum then over-subtracts, giving wrong values.

Always mark all four corners: +val at (r1,c1), -val at (r1,c2+1), -val at (r2+1,c1), +val at (r2+1,c2+1). The fourth corner corrects the double subtraction. Draw the rectangle and trace the prefix sum to verify.

09

Interview Strategy

Knowing when and how to use a difference array is half the battle. Here's how to present it in an interview setting to maximize your score.

The 5-Step Interview Flow

1

Identify the Range Update Pattern

'I see multiple operations that each add a value to a contiguous range. And we only need the final result, not intermediate states. This is a classic difference array problem.' Name the pattern early.

2

State the Brute Force and Its Cost

'The brute force updates every element in each range — O(n) per operation, O(n × q) total. With n = 20,000 and q = 20,000, that's 400 million operations — too slow.' Show you understand why optimization is needed.

3

Explain the Difference Array Insight

'Instead of updating every element, I mark the start and end of each range: diff[l] += val, diff[r+1] -= val. A prefix sum at the end reconstructs the final array. Each update is O(1), reconstruction is O(n). Total: O(n + q).' This is the key insight.

4

Code It Clean

Create diff with size n+1. Loop through updates. Compute prefix sum. Use clear variable names (diff, not d). Comment the +val and -val lines. Handle index conversion (1-indexed vs 0-indexed) explicitly.

5

Test & Analyze

Trace through a small example: 'For n=5, update [1,3,2]: diff = [0,2,0,0,-2,0]. Prefix sum = [0,2,2,2,0]. Correct!' State complexity: O(n + q) time, O(n) space. Mention the relationship to prefix sum.

Common Follow-Up Questions

Follow-UpHow to Handle
"What if I need the result after each update?"Difference array only gives the final state. For intermediate results, use a Segment Tree with lazy propagation — O(log n) per update and query. Or a Binary Indexed Tree for simpler cases.
"Can you extend this to 2D?"Yes — mark four corners of the rectangle using inclusion-exclusion: +val at (r1,c1), -val at (r1,c2+1) and (r2+1,c1), +val at (r2+1,c2+1). Then 2D prefix sum to reconstruct.
"What's the relationship to prefix sum?"They're inverses. Prefix sum: preprocess O(n), query O(1). Difference array: update O(1), reconstruct O(n). Prefix sum answers range queries; difference array handles range updates. diff[i] = arr[i] - arr[i-1], and prefixSum(diff) = arr.
"What if the range is very large but sparse?"Use a hash map (or sorted map) instead of an array. Store only the positions where changes occur. Sort the keys and sweep. This is the sweep line variant — same idea, sparse representation.
"Can this handle multiplicative updates?"No — difference arrays only work for additive updates. For multiplicative range updates, you'd need a Segment Tree with lazy propagation. The 'mark and sweep' trick only works because addition is associative and has an inverse (subtraction).
"How does this compare to a Segment Tree?"Difference array: O(1) update, O(n) reconstruct — batch only. Segment Tree: O(log n) update, O(log n) query — supports interleaved updates and queries. Use difference array when you can batch all updates before querying.

The meta-skill

Interviewers love difference array problems because they test whether you can see the "mark boundaries" optimization. The key sentence to say is: "Instead of updating every element in the range, I mark where the effect starts and stops. A prefix sum at the end propagates the effect. Each update is O(1) instead of O(n)." This single explanation covers the algorithm, the insight, and the complexity.

10

Summary & Cheat Sheet

Pin this section. Review it before mock interviews and contests.

Quick Revision Cheat Sheet

Core idea: Mark where effects start (+val at l) and stop (-val at r+1). Prefix sum reconstructs the actual values. O(1) per update, O(n) to reconstruct.

When to use: Multiple range updates, query final state once. Ask: 'Do I need intermediate results?' If no → difference array. If yes → segment tree.

The formula: diff[l] += val, diff[r+1] -= val. After all updates: result = prefixSum(diff). That's it — the entire pattern in one line.

Array size: Always create diff with size n+1 (or n+2). The extra slot prevents out-of-bounds when r = n-1 and you access diff[r+1] = diff[n].

Inverse of prefix sum: Prefix sum: preprocess O(n), query O(1). Difference array: update O(1), reconstruct O(n). They are mathematical inverses.

Sweep line variant: +1 at interval start, -1 at interval end. Sort events, sweep with running sum. Peak = max overlap. Used for meeting rooms, car pooling.

Tie-breaking: In sweep line: process ends (-1) before starts (+1) at the same time. This frees resources before claiming new ones.

Inclusive vs exclusive: Read carefully: does the effect include the right endpoint? If inclusive: cancel at r+1. If exclusive (like Car Pooling 'to'): cancel at r.

1-indexed trap: Convert 1-indexed to 0-indexed before applying. diff[first-1] += val, diff[last] -= val (for 1-indexed inclusive ranges).

2D extension: Four corners: +val at (r1,c1), -val at (r1,c2+1) and (r2+1,c1), +val at (r2+1,c2+1). Then 2D prefix sum.

Sparse ranges: When the range is huge but updates are few, use a Map instead of an array. Sort keys and sweep. Same idea, less memory.

Complexity: q updates on array of size n: O(q + n) total. Compare to brute force O(q × n). The savings are massive when both q and n are large.

Mental trigger: "Add val to range [l, r]" repeated many times → difference array. "How many intervals overlap at each point" → sweep line.

Decision Flowchart

When to Use Difference Array — Decision Treetext
Does the problem involve range updates (add val to [l, r])?
├── NONot a difference array problem
│         → Range queries? Use Prefix Sum
│         → Point updates + range queries? Use BIT or Segment Tree
└── YESDo you need the result after EACH update?
          ├── YESUse Segment Tree with lazy propagation (O(log n) per op)
          └── NOYou only need the final state after ALL updates
                    ├── Is the range small and dense (fits in an array)?
                    │   ├── YESBasic Difference Array (Template 1 or 2)
                    │   └── NOSweep Line with Map (Template 3)
                    └── Is it a 2D grid?
                        ├── YES → 2D Difference Array (Template 4)
                        └── NO  → 1D Difference Array

Difference Array vs. Prefix Sum — Quick Reference

Prefix SumDifference Array
PurposeFast range QUERIESFast range UPDATES
PreprocessO(n) to build prefix arrayO(1) per update (mark boundaries)
Query/ReconstructO(1) per range sum queryO(n) prefix sum to get final array
Best forMany queries, few/no updatesMany updates, query final state once
Relationshipprefix[i] = sum(arr[0..i])diff[i] = arr[i] - arr[i-1]
InverseprefixSum(diff) = arrdiff(prefixSum) = arr

One last thing

The difference array is one of the most elegant tricks in competitive programming and interviews. It turns a seemingly expensive operation (update every element in a range) into a trivially cheap one (mark two boundaries). The insight is simple: don't do the work now — record what needs to happen and let the prefix sum do the work later. This "lazy" approach is the same philosophy behind lazy propagation in segment trees, deferred execution in databases, and batch processing in distributed systems. Master this pattern and you'll see it everywhere.