Prefix Sums & Difference Arrays
Precompute once, answer range queries in O(1). The trick behind subarray-sum problems and range updates.
A prefix sum is a small precomputation that changes the shape of a problem. If you will ask many questions about sums of contiguous ranges, do the addition once up front and turn each later query into subtraction.
Difference arrays are the inverse trick. If many operations add a value across a range, mark only where the range starts and stops, then rebuild the final array with one prefix pass.
Both patterns are the same idea: store cumulative change so repeated range work becomes O(1) per query or update.
The mental model
Build prefix one slot longer than the input. prefix[i] means “sum of the first i elements,” so prefix[0] is 0 and the sum from index left through right is the total before right + 1 minus the total before left.
def build_prefix(nums):
prefix = [0]
for x in nums:
prefix.append(prefix[-1] + x)
return prefix
def range_sum(prefix, left, right):
return prefix[right + 1] - prefix[left]
That one extra leading zero removes most boundary bugs.
For range updates, use a difference array:
def apply_updates(n, updates):
diff = [0] * (n + 1)
for left, right, delta in updates:
diff[left] += delta
diff[right + 1] -= delta
nums = [0] * n
running = 0
for i in range(n):
running += diff[i]
nums[i] = running
return nums
Each update marks “start adding here” and “stop adding after right.” The final prefix pass applies all active deltas.
For a grid, a 2D prefix sum stores the sum of the rectangle from (0, 0) to each cell. A query rectangle uses inclusion-exclusion: big rectangle minus the strip above and the strip left, plus the corner you subtracted twice.
arrP · P[k] = P[k-1] + arr[k-1]sum(arr[2..5]) = P[6] − P[2] = 23 − 4 = 19direct sum = 19 ✓Pattern recognition
Variations
Worked problems
All three problems are about refusing to re-add the same range again.
Range Sum Query - Immutable
Design a small object that receives an integer list once and then answers many queries asking for the sum between two inclusive indices.
Approach. Store a leading-zero prefix array. Each query subtracts the sum before the left boundary from the sum through the right boundary.
Show solution
class NumArray:
def __init__(self, nums):
self.prefix = [0]
for x in nums:
self.prefix.append(self.prefix[-1] + x)
def sumRange(self, left, right):
return self.prefix[right + 1] - self.prefix[left]Complexity. O(n) preprocessing time and O(n) space. Each sumRange call is O(1) time.
Subarray Sum Equals K
Given an integer array and a target k, count how many contiguous subarrays sum to exactly k. Values may be negative.
Approach. Let prefix be the sum up to the current index. A subarray ending here has sum k if some earlier prefix was prefix - k. Keep a count of all prefix sums seen so far, starting with {0: 1} so subarrays beginning at index 0 are counted.
Show solution
from collections import defaultdict
def subarray_sum(nums, k):
seen = defaultdict(int)
seen[0] = 1
prefix = 0
total = 0
for x in nums:
prefix += x
total += seen[prefix - k]
seen[prefix] += 1
return totalComplexity. O(n) time, O(n) space. The hash map stores counts of prefix sums, not indices, because multiple starts can lead to the same ending position.
Range Addition
Start with an array of length n filled with zeroes. Each operation gives start, end, and inc, meaning every index in that inclusive range should increase by inc. Return the final array after all operations.
Approach. Mark the beginning and end of each update in a difference array. After all marks are placed, a running sum reconstructs the actual values.
Show solution
def get_modified_array(n, updates):
diff = [0] * (n + 1)
for start, end, inc in updates:
diff[start] += inc
diff[end + 1] -= inc
ans = [0] * n
running = 0
for i in range(n):
running += diff[i]
ans[i] = running
return ansComplexity. O(n + u) time and O(n) space, where u is the number of updates. Each range update is recorded in O(1).
When not to use it
Also watch the update/query mix. A plain prefix array is great for immutable data. If the underlying values change between queries, rebuilding the whole prefix array after every update throws away the benefit.