Facebook Pixel

370. Range Addition 🔒

Problem Description

You start with an array of zeros with a given length. You're given a series of updates where each update is a triplet [startIdx, endIdx, inc] that tells you to:

  • Add inc to every element from index startIdx to endIdx (inclusive)

Your task is to apply all these updates to the array and return the final result.

Example walkthrough:

  • If length = 5 and updates = [[1, 3, 2], [2, 4, 3]]
  • Start with: [0, 0, 0, 0, 0]
  • After first update (add 2 to indices 1-3): [0, 2, 2, 2, 0]
  • After second update (add 3 to indices 2-4): [0, 2, 5, 5, 3]
  • Return: [0, 2, 5, 5, 3]

The challenge is to find an efficient way to handle potentially many updates without actually iterating through each range for every update, which would be inefficient. The solution uses a difference array technique:

  1. Instead of updating ranges directly, mark the boundaries:

    • When adding c to range [l, r], mark d[l] += c (start of increase)
    • Mark d[r+1] -= c (end of increase) if r+1 is within bounds
  2. After marking all boundaries, compute the prefix sum of this difference array to get the final result. The prefix sum naturally propagates the increments across the ranges.

This approach reduces the time complexity from O(n × m) for naive range updates to O(n + m) where n is the array length and m is the number of updates.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Think about what happens when you add a value to a range. If you were to track the changes between consecutive elements rather than the elements themselves, adding a constant to a range creates a very specific pattern:

  • The difference increases by c at the start of the range
  • The difference decreases by c right after the range ends
  • All differences within the range remain unchanged

For example, if we add 3 to indices [1, 3] in array [0, 0, 0, 0, 0]:

  • Original: [0, 0, 0, 0, 0]
  • After update: [0, 3, 3, 3, 0]
  • Differences between consecutive elements: [0, +3, 0, 0, -3]

Notice how we only needed to mark two positions: where the increment starts (+3 at index 1) and where it ends (-3 at index 4).

The key insight is that instead of laboriously updating every element in each range, we can just mark these "boundary events" in a difference array. When we later compute the prefix sum of this difference array, the increments naturally "flow" across their ranges:

  • The +c at the start propagates through the prefix sum
  • The -c after the end cancels it out
  • Multiple overlapping ranges automatically combine through addition

This transforms the problem from "update many elements many times" to "mark boundaries, then reconstruct once" - a much more efficient approach. The prefix sum acts like a running total that accumulates all the boundary changes we've marked, giving us the final array values.

Solution Approach

The implementation uses a difference array technique to efficiently handle range updates:

Step 1: Initialize the difference array

d = [0] * length

Create a difference array d of the same size as our target array, initialized with zeros.

Step 2: Mark boundaries for each update

for l, r, c in updates:
    d[l] += c
    if r + 1 < length:
        d[r + 1] -= c

For each update [l, r, c]:

  • Add c to d[l] - this marks where the increment starts
  • Subtract c from d[r + 1] (if within bounds) - this marks where the increment ends

The boundary check if r + 1 < length prevents index out of bounds when the range extends to the last element.

Step 3: Compute prefix sum to get final array

return list(accumulate(d))

Use Python's accumulate function to compute the running sum of the difference array. This reconstructs the final array by propagating all the marked changes.

Example walkthrough:

  • length = 5, updates = [[1,3,2], [2,4,3]]
  • Initial difference array: [0, 0, 0, 0, 0]
  • After first update [1,3,2]: d = [0, 2, 0, 0, -2]
  • After second update [2,4,3]: d = [0, 2, 3, 0, -2] (note: d[5] would be -3 but it's out of bounds)
  • Prefix sum: [0, 2, 5, 5, 3]

Time Complexity: O(n + m) where n is the array length and m is the number of updates Space Complexity: O(n) for the difference array

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's trace through a small example to see how the difference array technique works.

Given: length = 4, updates = [[0,2,3], [1,3,2]]

Step 1: Initialize difference array

d = [0, 0, 0, 0]

Step 2: Process first update [0,2,3]

  • Add 3 to range [0,2]
  • Mark d[0] += 3 (start of range)
  • Mark d[3] -= 3 (end of range + 1)
d = [3, 0, 0, -3]

Step 3: Process second update [1,3,2]

  • Add 2 to range [1,3]
  • Mark d[1] += 2 (start of range)
  • d[4] would be -= 2, but index 4 is out of bounds, so skip
d = [3, 2, 0, -3]

Step 4: Compute prefix sum

  • Position 0: 3 → result[0] = 3
  • Position 1: 3 + 2 = 5 → result[1] = 5
  • Position 2: 5 + 0 = 5 → result[2] = 5
  • Position 3: 5 + (-3) = 2 → result[3] = 2

Final result: [3, 5, 5, 2]

Verification with naive approach:

  • Start: [0, 0, 0, 0]
  • After [0,2,3]: [3, 3, 3, 0]
  • After [1,3,2]: [3, 5, 5, 2]

The difference array elegantly handles overlapping ranges - notice how position 1 and 2 automatically combine both updates through the prefix sum computation.

Solution Implementation

1from typing import List
2from itertools import accumulate
3
4class Solution:
5    def getModifiedArray(self, length: int, updates: List[List[int]]) -> List[int]:
6        # Initialize difference array with zeros
7        # This array will store the differences between consecutive elements
8        difference_array = [0] * length
9      
10        # Process each update operation [start_index, end_index, increment_value]
11        for start_idx, end_idx, increment in updates:
12            # Add increment at the start position
13            # This marks the beginning of the range update
14            difference_array[start_idx] += increment
15          
16            # Subtract increment at position after end_idx
17            # This marks the end of the range update
18            if end_idx + 1 < length:
19                difference_array[end_idx + 1] -= increment
20      
21        # Apply prefix sum to get the final array
22        # The cumulative sum of differences gives us the actual values
23        return list(accumulate(difference_array))
24
1class Solution {
2    public int[] getModifiedArray(int length, int[][] updates) {
3        // Initialize difference array to track range updates
4        int[] differenceArray = new int[length];
5      
6        // Process each update operation
7        for (int[] update : updates) {
8            int startIndex = update[0];
9            int endIndex = update[1];
10            int increment = update[2];
11          
12            // Mark the start of the range with positive increment
13            differenceArray[startIndex] += increment;
14          
15            // Mark the position after the end of range with negative increment
16            // This ensures the increment doesn't affect elements beyond the range
17            if (endIndex + 1 < length) {
18                differenceArray[endIndex + 1] -= increment;
19            }
20        }
21      
22        // Apply prefix sum to get the final array values
23        // Each position accumulates all the increments that affect it
24        for (int i = 1; i < length; ++i) {
25            differenceArray[i] += differenceArray[i - 1];
26        }
27      
28        return differenceArray;
29    }
30}
31
1class Solution {
2public:
3    vector<int> getModifiedArray(int length, vector<vector<int>>& updates) {
4        // Initialize difference array with all zeros
5        vector<int> differenceArray(length);
6      
7        // Process each update operation
8        for (const auto& update : updates) {
9            int startIndex = update[0];
10            int endIndex = update[1];
11            int increment = update[2];
12          
13            // Mark the start of the range with positive increment
14            differenceArray[startIndex] += increment;
15          
16            // Mark the position after the end of range with negative increment
17            // to cancel out the effect for subsequent elements
18            if (endIndex + 1 < length) {
19                differenceArray[endIndex + 1] -= increment;
20            }
21        }
22      
23        // Convert difference array to actual result array using prefix sum
24        // Each element becomes the cumulative sum up to that position
25        for (int i = 1; i < length; ++i) {
26            differenceArray[i] += differenceArray[i - 1];
27        }
28      
29        return differenceArray;
30    }
31};
32
1/**
2 * Applies range update operations to an array and returns the final result.
3 * Uses difference array technique for efficient range updates.
4 * 
5 * @param length - The length of the target array
6 * @param updates - Array of update operations, each containing [startIndex, endIndex, incrementValue]
7 * @returns The modified array after applying all updates
8 */
9function getModifiedArray(length: number, updates: number[][]): number[] {
10    // Initialize difference array with all zeros
11    const differenceArray: number[] = Array(length).fill(0);
12  
13    // Apply range updates using difference array technique
14    // Mark the start and end+1 positions for each update
15    for (const [startIndex, endIndex, incrementValue] of updates) {
16        // Add increment value at the start of the range
17        differenceArray[startIndex] += incrementValue;
18      
19        // Subtract increment value at position after the end of range
20        // This ensures the increment only applies within [startIndex, endIndex]
21        if (endIndex + 1 < length) {
22            differenceArray[endIndex + 1] -= incrementValue;
23        }
24    }
25  
26    // Calculate prefix sum to get the final array values
27    // Each position accumulates all the differences up to that point
28    for (let index = 1; index < length; ++index) {
29        differenceArray[index] += differenceArray[index - 1];
30    }
31  
32    return differenceArray;
33}
34

Time and Space Complexity

The time complexity is O(n + m), where n is the length of the array and m is the number of updates. The algorithm iterates through all m updates to populate the difference array (taking O(m) time), then performs a prefix sum operation using accumulate() which takes O(n) time to traverse the entire array.

The space complexity is O(n). The algorithm creates a difference array d of size n to store the incremental changes, and the final result array returned by accumulate() also requires O(n) space. No additional data structures that scale with input size are used.

Common Pitfalls

1. Index Out of Bounds When Marking End Boundary

The Pitfall: The most common mistake is forgetting to check if end_idx + 1 is within the array bounds before updating difference_array[end_idx + 1]. This leads to an IndexError when the update range extends to the last element of the array.

Incorrect Code:

for start_idx, end_idx, increment in updates:
    difference_array[start_idx] += increment
    difference_array[end_idx + 1] -= increment  # ❌ IndexError when end_idx == length - 1

Correct Solution:

for start_idx, end_idx, increment in updates:
    difference_array[start_idx] += increment
    if end_idx + 1 < length:  # ✅ Check bounds first
        difference_array[end_idx + 1] -= increment

2. Misunderstanding the Difference Array Concept

The Pitfall: Some developers try to directly update the range in each iteration, missing the entire point of the difference array optimization.

Incorrect Approach:

result = [0] * length
for start_idx, end_idx, increment in updates:
    for i in range(start_idx, end_idx + 1):  # ❌ O(n*m) complexity
        result[i] += increment
return result

Why It's Wrong: This defeats the purpose of using a difference array and results in O(n × m) time complexity instead of O(n + m).

3. Off-by-One Error in Range Boundaries

The Pitfall: Confusing whether the end index is inclusive or exclusive, leading to incorrect boundary marking.

Common Mistake:

# Thinking end_idx is exclusive and marking wrong boundary
difference_array[start_idx] += increment
if end_idx < length:  # ❌ Wrong condition
    difference_array[end_idx] -= increment  # ❌ Wrong index

Remember: The problem states end_idx is inclusive, so we need to mark the boundary at end_idx + 1 to stop the increment effect after position end_idx.

4. Not Returning the Accumulated Result

The Pitfall: Returning the difference array itself instead of its prefix sum.

Incorrect:

return difference_array  # ❌ Returns differences, not final values

Correct:

return list(accumulate(difference_array))  # ✅ Returns cumulative sum
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

What's the relationship between a tree and a graph?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More