Prefix Sum & Difference Arrays

intermediate prefix-sum difference-array range-query pattern

Prefix sum is a technique where we precompute cumulative sums so we can answer “what’s the sum from index i to index j?” in O(1) time. Without it, every range sum query takes O(n). With it, we do O(n) work once, and then every query is instant.

Think of it like keeping a running total. Instead of adding up numbers every time someone asks, we maintain a list of “sum so far” values and just subtract to get any range.

Building a Prefix Sum Array

Given an array nums, the prefix sum array prefix[i] = sum of all elements from index 0 to i.

Prefix Sum Construction
Original:
2 4 1 3 5
↓ cumulative sums ↓
Prefix:
2 6 7 10 15
[0] [1] [2] [3] [4]
prefix[3] = 2 + 4 + 1 + 3 = 10
function buildPrefixSum(nums) {
  const prefix = new Array(nums.length);
  prefix[0] = nums[0];
  for (let i = 1; i < nums.length; i++) {
    prefix[i] = prefix[i - 1] + nums[i]; // running total
  }
  return prefix;
}

console.log(buildPrefixSum([2, 4, 1, 3, 5])); // [2, 6, 7, 10, 15]
def build_prefix_sum(nums):
    prefix = [0] * len(nums)
    prefix[0] = nums[0]
    for i in range(1, len(nums)):
        prefix[i] = prefix[i - 1] + nums[i]  # running total
    return prefix

print(build_prefix_sum([2, 4, 1, 3, 5]))  # [2, 6, 7, 10, 15]
public static int[] buildPrefixSum(int[] nums) {
    int[] prefix = new int[nums.length];
    prefix[0] = nums[0];
    for (int i = 1; i < nums.length; i++) {
        prefix[i] = prefix[i - 1] + nums[i]; // running total
    }
    return prefix;
}
// [2, 4, 1, 3, 5] → [2, 6, 7, 10, 15]

Time: O(n) to build, O(1) per query after that.

Range Sum Query

Once we have the prefix sum array, getting the sum of any range [left, right] is just one subtraction:

sum(left, right) = prefix[right] - prefix[left - 1]

If left is 0, then sum(0, right) = prefix[right].

function rangeSum(prefix, left, right) {
  if (left === 0) return prefix[right];
  return prefix[right] - prefix[left - 1];
}

const prefix = buildPrefixSum([2, 4, 1, 3, 5]);
console.log(rangeSum(prefix, 1, 3)); // 4 + 1 + 3 = 8
console.log(rangeSum(prefix, 0, 4)); // 2 + 4 + 1 + 3 + 5 = 15
console.log(rangeSum(prefix, 2, 2)); // 1 (single element)
def range_sum(prefix, left, right):
    if left == 0:
        return prefix[right]
    return prefix[right] - prefix[left - 1]

prefix = build_prefix_sum([2, 4, 1, 3, 5])
print(range_sum(prefix, 1, 3))  # 4 + 1 + 3 = 8
print(range_sum(prefix, 0, 4))  # 2 + 4 + 1 + 3 + 5 = 15
print(range_sum(prefix, 2, 2))  # 1 (single element)
public static int rangeSum(int[] prefix, int left, int right) {
    if (left == 0) return prefix[right];
    return prefix[right] - prefix[left - 1];
}

// prefix = [2, 6, 7, 10, 15]
// rangeSum(prefix, 1, 3) → 10 - 2 = 8 ✓
// rangeSum(prefix, 0, 4) → 15 ✓

Pro tip: Some people prefer building the prefix array with an extra 0 at the beginning: prefix[0] = 0, prefix[1] = nums[0], .... This eliminates the left === 0 edge case because the formula becomes prefix[right + 1] - prefix[left] always.

Subarray Sum Equals K

This is one of the most popular prefix sum interview problems (LeetCode 560). Given an array and a target k, find the number of subarrays that sum to k.

The key insight: if prefixSum[j] - prefixSum[i] = k, then the subarray from i+1 to j sums to k. So we need to count how many times prefixSum[j] - k has appeared before.

We use a hash map to track how many times each prefix sum has occurred.

function subarraySum(nums, k) {
  const prefixCount = new Map(); // prefix sum → count
  prefixCount.set(0, 1);  // empty prefix has sum 0

  let sum = 0;
  let count = 0;

  for (const num of nums) {
    sum += num;
    // if (sum - k) exists, there's a subarray ending here with sum k
    if (prefixCount.has(sum - k)) {
      count += prefixCount.get(sum - k);
    }
    prefixCount.set(sum, (prefixCount.get(sum) || 0) + 1);
  }
  return count;
}

console.log(subarraySum([1, 1, 1], 2));     // 2 ([1,1] at two positions)
console.log(subarraySum([1, 2, 3], 3));     // 2 ([1,2] and [3])
def subarray_sum(nums, k):
    prefix_count = {0: 1}  # prefix sum → count
    current_sum = 0
    count = 0

    for num in nums:
        current_sum += num
        # if (sum - k) exists, there's a subarray ending here with sum k
        if current_sum - k in prefix_count:
            count += prefix_count[current_sum - k]
        prefix_count[current_sum] = prefix_count.get(current_sum, 0) + 1

    return count

print(subarray_sum([1, 1, 1], 2))   # 2
print(subarray_sum([1, 2, 3], 3))   # 2
public static int subarraySum(int[] nums, int k) {
    Map<Integer, Integer> prefixCount = new HashMap<>();
    prefixCount.put(0, 1); // empty prefix has sum 0

    int sum = 0, count = 0;

    for (int num : nums) {
        sum += num;
        if (prefixCount.containsKey(sum - k)) {
            count += prefixCount.get(sum - k);
        }
        prefixCount.merge(sum, 1, Integer::sum);
    }
    return count;
}
// [1, 1, 1] with k=2 → 2

Time: O(n), Space: O(n) — the hash map + prefix sum combo turns an O(n²) problem into O(n).

Difference Array Technique

Prefix sum is for range queries. The difference array is for range updates.

Imagine we have an array of zeros and we need to add 5 to every element from index 2 to 5. Then add 3 to every element from index 1 to 4. Doing this naively is O(n) per update. With a difference array, each update is O(1).

The idea: instead of updating every element in the range, we mark the start and end+1 of the range. Then we compute the prefix sum at the end to get the final array.

function applyRangeUpdates(n, updates) {
  const diff = new Array(n + 1).fill(0); // difference array

  for (const [left, right, value] of updates) {
    diff[left] += value;        // start adding here
    diff[right + 1] -= value;   // stop adding after here
  }

  // build result from difference array (prefix sum)
  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;
}

// Array of size 5, two updates: add 5 to [1,3], add 3 to [2,4]
console.log(applyRangeUpdates(5, [[1, 3, 5], [2, 4, 3]]));
// [0, 5, 8, 8, 3]
def apply_range_updates(n, updates):
    diff = [0] * (n + 1)  # difference array

    for left, right, value in updates:
        diff[left] += value       # start adding here
        diff[right + 1] -= value  # stop adding after here

    # build result from difference array (prefix sum)
    result = [0] * n
    result[0] = diff[0]
    for i in range(1, n):
        result[i] = result[i - 1] + diff[i]
    return result

# Array of size 5, two updates: add 5 to [1,3], add 3 to [2,4]
print(apply_range_updates(5, [[1, 3, 5], [2, 4, 3]]))
# [0, 5, 8, 8, 3]
public static int[] applyRangeUpdates(int n, int[][] updates) {
    int[] diff = new int[n + 1]; // difference array

    for (int[] update : updates) {
        diff[update[0]] += update[2];       // start adding here
        diff[update[1] + 1] -= update[2];   // stop adding after here
    }

    // build result from difference array (prefix sum)
    int[] result = new int[n];
    result[0] = diff[0];
    for (int i = 1; i < n; i++) {
        result[i] = result[i - 1] + diff[i];
    }
    return result;
}
// n=5, updates=[[1,3,5],[2,4,3]] → [0, 5, 8, 8, 3]

Each update is O(1). Final reconstruction is O(n). So for q updates on an array of size n, it’s O(n + q) instead of O(n * q).

When to Use These Techniques

TechniqueUse WhenComplexity
Prefix SumMany range sum queries on a static arrayO(n) build + O(1) per query
Prefix Sum + Hash MapCount subarrays with a target sumO(n) time + O(n) space
Difference ArrayMany range update operations, query at the endO(1) per update + O(n) to reconstruct

In simple language, prefix sum is about doing homework upfront so we don’t repeat work later. We spend O(n) once to build the prefix array, and then every range query is free (O(1)). The difference array flips this idea — instead of querying ranges efficiently, it lets us update ranges efficiently. Both are must-know patterns for coding interviews.