LeetVision Docs Back to Main Site
LeetVision Docs
© 2026 LeetVision. All rights reserved.
HomeAboutContact
Stage 1 — Fundamentals/Arrays

Difference Array Technique

Easy 8 min read

Difference Array Technique

Introduction

The Difference Array technique is an optimization strategy used to efficiently handle multiple range update operations on an array. It transforms range updates into constant-time point updates on an auxiliary array, which can then be used to reconstruct the original array. This method is particularly useful when the final state of the array after all updates is required, rather than individual elements during updates.

Quick Overview

Efficiently performs multiple range additions/subtractions on an array in O(1) time per update by modifying a difference array, then reconstructs the final array in O(N).

Motivation

Imagine an array representing daily stock prices, and you need to apply multiple price adjustments over various date ranges. A naive approach of iterating through each range and updating elements would lead to O(N*Q) time complexity for Q queries on an array of size N, which is inefficient for large N or Q. The Difference Array provides a way to reduce the cost of each range update to O(1), making it highly effective for scenarios where many range updates are followed by a single reconstruction or query phase.

Real Life Analogy

Consider a long row of identical streetlights, all initially off. You have a list of instructions: 'Turn on all lights from pole A to pole B,' or 'Dim lights from pole C to pole D by X amount.' Instead of walking along the street for each instruction, which would be tedious, you decide to use a simpler method. For each 'turn on from A to B' instruction, you just place a small 'start turning on' note at pole A and a 'stop turning on' note at pole B+1. For 'dim from C to D,' you place a 'start dimming by X' note at C and 'stop dimming by X' note at D+1. After collecting all notes, you walk down the street just once. When you reach a pole, you check its notes, apply all pending changes (like accumulating brightness or dimness), and then determine its final state. The 'notes' represent the difference array, and the final walk-through is the prefix sum operation.

Core Idea

Construct an auxiliary array, diff, where diff[i] = arr[i] - arr[i-1]. A range update arr[L...R] by val can then be achieved by diff[L] += val and diff[R+1] -= val. The original array can be reconstructed by performing a prefix sum on the diff array.

Key Insight

An update arr[L...R] += val only affects the differences at L and R+1. Adding val to arr[L] increases arr[L] - arr[L-1] by val. Subtracting val from arr[R+1] (or specifically, decreasing arr[R+1] - arr[R] by val) effectively 'stops' the propagation of the added val beyond index R. Everything between L and R inherently receives the val through the prefix sum property.

Prerequisites

  • Basic Array Manipulation
  • Prefix Sums (Cumulative Sum)

Mathematical Explanation

Let A be the original array of size N. Let D be the difference array of size N+1 (or N if considering a 0-indexed diff[0]=A[0] and diff[i]=A[i]-A[i-1] for i>0).

D[0] = A[0] D[i] = A[i] - A[i-1] for i > 0

To reconstruct A from D: A[0] = D[0] A[i] = D[i] + A[i-1] for i > 0 This is equivalent to A[i] = sum(D[0...i]).

Consider a range update A[L...R] += val:

  1. A[L] increases by val. This directly affects D[L] = A[L] - A[L-1], so D[L] must increase by val.
  2. For i such that L < i <= R, A[i] increases by val and A[i-1] also increases by val. Thus, D[i] = A[i] - A[i-1] remains unchanged. The difference A[i] - A[i-1] is preserved within the range [L+1, R] because both terms change by the same amount.
  3. At R+1, A[R+1] is not increased by val, but A[R] is. This means D[R+1] = A[R+1] - A[R] will effectively decrease by val. To counteract this implicit decrease and ensure that only A[L...R] are affected, we explicitly set D[R+1] -= val. If R+1 is out of bounds (i.e., R+1 >= N), this step is skipped as there's no element A[R+1] whose difference with A[R] needs adjustment.

Therefore, a range update A[L...R] += val translates to: D[L] += val If R+1 < N (where N is the original array's length), then D[R+1] -= val.

Visualization

Original Array `arr` (size 5):
`arr = [10, 20, 30, 40, 50]`

Initial Difference Array `diff` (size 6 for R+1 handling, where `diff[i] = arr[i] - arr[i-1]` and `diff[0]=arr[0]`):
`diff = [10, 10, 10, 10, 10, 0]`

Range Update: `arr[1...3] += 5` (indices 1, 2, 3)

1. Update `diff[L]`: `diff[1] += 5`
   `diff` becomes `[10, 15, 10, 10, 10, 0]`

2. Update `diff[R+1]`: `diff[3+1] -= 5` => `diff[4] -= 5`
   `diff` becomes `[10, 15, 10, 10, 5, 0]`

Reconstructing the array from the updated `diff`:
(Current prefix sum `current_val` starting from `diff[0]`)
`arr[0] = diff[0] = 10`
`arr[1] = diff[1] + arr[0] = 15 + 10 = 25`
`arr[2] = diff[2] + arr[1] = 10 + 25 = 35`
`arr[3] = diff[3] + arr[2] = 10 + 35 = 45`
`arr[4] = diff[4] + arr[3] = 5 + 45 = 50`

Final `arr`: `[10, 25, 35, 45, 50]` (Correct: `arr[1], arr[2], arr[3]` increased by 5)

Dry Run

Let's trace arr = [1, 2, 3, 4, 5] and updates:

  1. (L=0, R=2, val=+10)

  2. (L=1, R=3, val=-5)

  3. Initialize Difference Array diff: arr (conceptual): [1, 2, 3, 4, 5] N = 5. diff array will be size N+1 = 6. diff[0] = arr[0] = 1 diff[1] = arr[1] - arr[0] = 2 - 1 = 1 diff[2] = arr[2] - arr[1] = 3 - 2 = 1 diff[3] = arr[3] - arr[2] = 4 - 3 = 1 diff[4] = arr[4] - arr[3] = 5 - 4 = 1 diff[5] (sentinel) = 0 (implicitly, or explicitly initialized) Current diff = [1, 1, 1, 1, 1, 0]

  4. Apply Update 1: (L=0, R=2, val=+10) diff[L] += val => diff[0] += 10 diff[0] becomes 1 + 10 = 11 diff[R+1] -= val => diff[2+1] -= 10 => diff[3] -= 10 diff[3] becomes 1 - 10 = -9 Current diff = [11, 1, 1, -9, 1, 0]

  5. Apply Update 2: (L=1, R=3, val=-5) diff[L] += val => diff[1] += -5 diff[1] becomes 1 - 5 = -4 diff[R+1] -= val => diff[3+1] -= -5 => diff[4] -= -5 => diff[4] += 5 diff[4] becomes 1 + 5 = 6 Current diff = [11, -4, 1, -9, 6, 0]

  6. Reconstruct Original Array arr_final: result_arr of size N=5. result_arr[0] = diff[0] = 11 result_arr[1] = diff[1] + result_arr[0] = -4 + 11 = 7 result_arr[2] = diff[2] + result_arr[1] = 1 + 7 = 8 result_arr[3] = diff[3] + result_arr[2] = -9 + 8 = -1 result_arr[4] = diff[4] + result_arr[3] = 6 + (-1) = 5

    Final arr_final = [11, 7, 8, -1, 5]

    Verification: Initial: [1, 2, 3, 4, 5] After update (0, 2, +10): [11, 12, 13, 4, 5] After update (1, 3, -5): [11, 7, 8, -1, 5] (Matches!)

Algorithm

  • Initialize a difference array diff of size N+1 (where N is the size of the original array), with all elements set to 0.
  • If an initial array A is provided, populate diff: Set diff[0] = A[0]. For i from 1 to N-1, set diff[i] = A[i] - A[i-1].
  • To apply a range update (L, R, val): Add val to diff[L]. If R + 1 < N (i.e., R+1 is a valid index in the conceptual original array), subtract val from diff[R + 1].
  • After all updates, to reconstruct the final array result_arr of size N: Set result_arr[0] = diff[0]. For i from 1 to N-1, set result_arr[i] = diff[i] + result_arr[i-1].

Pseudocode

FUNCTION initializeDiffArray(arr, N):
    diff = array of size N+1, initialized to 0
    IF N > 0:
        diff[0] = arr[0]
        FOR i FROM 1 TO N-1:
            diff[i] = arr[i] - arr[i-1]
    RETURN diff

FUNCTION applyRangeUpdate(diff, N, L, R, val):
    diff[L] = diff[L] + val
    IF R + 1 < N: // R+1 must be a valid index in the conceptual original array
        diff[R + 1] = diff[R + 1] - val

FUNCTION reconstructArray(diff, N):
    result_arr = array of size N
    IF N > 0:
        result_arr[0] = diff[0]
        FOR i FROM 1 TO N-1:
            result_arr[i] = diff[i] + result_arr[i-1]
    RETURN result_arr

// Example Usage:
// arr = [10, 20, 30, 40, 50]
// N = 5
// diff = initializeDiffArray(arr, N)
// // diff is now [10, 10, 10, 10, 10, 0]

// applyRangeUpdate(diff, N, 1, 3, 5) // arr[1...3] += 5
// // diff is now [10, 15, 10, 10, 5, 0]

// final_arr = reconstructArray(diff, N)
// // final_arr is now [10, 25, 35, 45, 50]

Code

(See Implementations block below)

Time Complexity

O(N + Q) - Initializing the difference array from an original array takes O(N) time. Each range update operation (L, R, val) takes O(1) time as it only involves two array accesses and updates. Reconstructing the final array from the difference array requires a single pass, which takes O(N) time. Therefore, if there are Q update queries, the total time complexity is O(N) (initialization) + O(Q) (updates) + O(N) (reconstruction), simplifying to O(N + Q). This is a significant improvement over the O(N * Q) complexity of a naive approach.

Space Complexity

O(N) - The difference array diff requires N+1 elements to store the differences and handle the sentinel for the R+1 case, where N is the size of the original array. This constitutes O(N) additional space.

Advantages

  • Efficiency for Multiple Range Updates: Transforms O(N) range updates into O(1) point updates on the difference array, drastically improving performance for many updates.
  • Simplicity: Conceptually simpler to implement than more complex data structures like segment trees or Fenwick trees for this specific use case.
  • Memory-Efficient (for its purpose): Requires only O(N) auxiliary space, which is optimal for storing modifications across the array.

Limitations

  • No Point Queries During Updates: Cannot efficiently query the value of an element at a specific index after some updates but before all updates are applied and the array is reconstructed. The full array must be reconstructed to get current values.
  • Not for Dynamic Array Sizes: Primarily designed for static array sizes where N is known beforehand. Resizing would invalidate difference relationships.
  • Only for Sum/Difference Operations: Best suited for operations that involve adding or subtracting a value over a range. Not directly applicable for other range operations like minimum, maximum, multiplication, etc., without significant modification or different data structures.

Common Mistakes

  • Off-by-one Errors: Incorrectly handling L, R, and R+1 indices, especially when R+1 is at the boundary or out of bounds. The diff array should often be N+1 length to safely handle diff[R+1] for R=N-1.
  • Incorrect Initialization: Forgetting to initialize diff[0] correctly with arr[0] or not considering an empty initial array.
  • Not Understanding Prefix Sums: Failing to correctly reconstruct the original array by performing the prefix sum operation on the difference array.
  • Applying for Wrong Problems: Using it when frequent point queries are needed, or for range operations other than addition/subtraction.

Edge Cases

  • Empty Array (N=0): The implementation should gracefully handle an empty input array, typically resulting in an empty or single-element difference array of zeros.
  • Single Element Array (N=1): An update (0, 0, val) should correctly affect diff[0] and, if applicable, diff[1] (the sentinel for N=1).
  • Full Array Update (0, N-1, val): This means diff[0] += val and diff[N] -= val. The diff[N] entry (the N in N+1 size) correctly handles the 'stop' outside the array boundary.
  • Overlapping Updates: Handled naturally by repeated additions/subtractions to the same diff array indices; the cumulative effect is correctly captured.
  • Negative Values/Updates: The technique works correctly with negative initial values and negative update values.

Recognition Tricks

  • Keywords like 'range increment', 'range decrement', 'update interval', or 'all elements in a segment'.
  • Problems involving many updates to continuous subsegments of an array.
  • The requirement is to find the final state of the array after all updates, or perform a single pass/query on the array after all updates.
  • When a naive O(N*Q) solution is too slow, and a specific type of range query (sum/difference) is involved, making O(N+Q) highly desirable.

When To Use

  • When you need to perform multiple range addition or subtraction operations on an array.
  • When you only need to query or access the elements of the array after all updates have been applied, not during intermediate stages.
  • When N (array size) and Q (number of queries) are large, and O(N+Q) is acceptable, but O(N*Q) is not.
  • As a simpler and more memory-efficient alternative to more complex data structures like Segment Trees or Fenwick Trees when only range sum/difference updates are required without interleaved range queries.

Alternative Approaches

  • Naive Iteration: For each update (L, R, val), iterate from L to R and update arr[i] += val. Time complexity: O(N*Q). Not feasible for large N or Q.
  • Segment Tree: A powerful data structure supporting range updates and range queries (like sum, min, max) in O(log N) time per operation. More complex to implement but more versatile, especially with lazy propagation.
  • Fenwick Tree (BIT): Supports point updates and prefix sum queries in O(log N) time. Can be adapted for range updates and point queries, or range updates and range queries with some cleverness, often O(log N) time per operation, but more intricate for range updates.
  • Lazy Propagation (with Segment Tree): Extends Segment Trees to handle range updates efficiently by deferring updates to child nodes until necessary, maintaining O(log N) per query/update, but significantly more complex.

Interview Perspective

Interviewers often use Difference Arrays to gauge a candidate's understanding of optimization techniques beyond brute force. It tests the ability to recognize patterns where direct application is costly and to leverage mathematical properties (like prefix sums) for efficiency. It's considered a foundational technique, often serving as a stepping stone to more complex data structures like Segment Trees with lazy propagation. Expect questions that can be solved naively in O(N*Q) and require optimization to O(N+Q), or as a precursor to a Segment Tree question. Understanding the trade-offs (e.g., no intermediate point queries) is crucial.

Key Takeaways

  • The Difference Array is an elegant technique for efficient range updates (addition/subtraction) on an array.
  • It leverages the concept that a range update arr[L...R] += val can be mapped to two constant-time point updates on a difference array: diff[L] += val and diff[R+1] -= val.
  • The original array's final state is reconstructed by taking the prefix sums of the difference array.
  • Its main strength lies in scenarios with many range updates followed by a single full array reconstruction or query, achieving O(N+Q) complexity.
  • Be mindful of array bounds and off-by-one errors, especially when handling the R+1 index and the diff array's size.

Practice Problems

(See Practice Problems index below)

Implementations

class DifferenceArray:
    def __init__(self, arr):
        self.n = len(arr)
        self.diff = [0] * (self.n + 1)
        if self.n > 0:
            self.diff[0] = arr[0]
            for i in range(1, self.n):
                self.diff[i] = arr[i] - arr[i-1]

    def update_range(self, L, R, val):
        self.diff[L] += val
        if R + 1 < self.n:
            self.diff[R + 1] -= val

    def get_result_array(self):
        result_arr = [0] * self.n
        if self.n > 0:
            result_arr[0] = self.diff[0]
            for i in range(1, self.n):
                result_arr[i] = self.diff[i] + result_arr[i-1]
        return result_arr

Practice Problems

LeetCode ProblemIDDifficultyPatternOfficial LinkLeet Vision Solution
Corporate Flight Bookings1109MediumDifference Array, Prefix SumSolve on LeetCode ↗Video Solution ↗
Car Pooling1094MediumDifference Array, Prefix Sum, Interval SumSolve on LeetCode ↗Video Solution ↗
Range Addition370MediumDifference ArraySolve on LeetCode ↗Video Solution ↗
Maximum Population Year1854EasyDifference Array, Sweep Line, Prefix SumSolve on LeetCode ↗Video Solution ↗
PreviousNext

Related Modules

Complexity AnalysisStringsHashing