Skip to main content

Range Addition

MediumProblemExternal link

Description

You get an integer length and a list of updates. Start from an array of length zeros.

Each update is [start, end, inc]: add inc to every value from index start to index end, both included.

Return the array after all the updates have been applied.

Examples

Example 1

Input: length = 5, updates = [[1, 3, 2], [2, 4, 3], [0, 2, -2]]

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

Explanation: After [1, 3, 2]: [0, 2, 2, 2, 0]. After [2, 4, 3]: [0, 2, 5, 5, 3]. After [0, 2, -2]: [-2, 0, 3, 5, 3].

Example 2

Input: length = 10, updates = [[2, 4, 6], [5, 6, 8], [1, 9, -4]]

Output: [0,-4,2,2,2,4,4,-4,-4,-4]

Explanation: Indexes 2 to 4 get 6, indexes 5 and 6 get 8, and every index from 1 to 9 loses 4.

Example 3

Input: length = 3, updates = []

Output: [0,0,0]

Explanation: No updates, so the array stays all zeros.

Constraints

  • 1 ≤ length ≤ 10^5
  • 0 ≤ updates.length ≤ 10^4
  • 0 ≤ start ≤ end < length
  • -1000 ≤ inc ≤ 1000

Editorial

Brute Force

Intuition

Do what each update says: walk its range and add inc to every index in it.

Step-by-Step Explanation

For length = 5 and updates = [[1, 3, 2], [2, 4, 3], [0, 2, -2]]:

  1. Start from 5 zeros and apply each update to every index of its range.
  2. Add 2 to indexes 1 to 3, one index at a time. That's 3 additions, 3 so far.
  3. Add 3 to indexes 2 to 4, one index at a time. That's 3 additions, 6 so far.
  4. Add −2 to indexes 0 to 2, one index at a time. That's 3 additions, 9 so far.
  5. Done after 9 additions, since each update cost as much as its range is long.

Apply Each Update Cell by Cell — The shaded cells are the range of the update just applied.

Algorithm

  1. Make result, length zeros.
  2. For each update, add inc to result[start] through result[end].
  3. Return result.

Code

class Solution {
public:
    vector<int> getModifiedArray(int length, vector<vector<int>>& updates) {
        vector<int> result(length, 0);
        for (const auto& u : updates)
            for (int i = u[0]; i <= u[1]; i++)  // every index of the range, one at a time
                result[i] += u[2];
        return result;
    }
};
class Solution:
    def getModifiedArray(self, length: int, updates: list[list[int]]) -> list[int]:
        result = [0] * length
        for start, end, inc in updates:
            for i in range(start, end + 1):     # every index of the range, one at a time
                result[i] += inc
        return result
class Solution {
    public int[] getModifiedArray(int length, int[][] updates) {
        int[] result = new int[length];
        for (int[] u : updates)
            for (int i = u[0]; i <= u[1]; i++)  // every index of the range, one at a time
                result[i] += u[2];
        return result;
    }
}
var getModifiedArray = function(length, updates) {
    const result = new Array(length).fill(0);
    for (const [start, end, inc] of updates)
        for (let i = start; i <= end; i++)      // every index of the range, one at a time
            result[i] += inc;
    return result;
};

Complexity Analysis

Time Complexity: O(n · q)

Each of the q updates can cover all n indexes.

Space Complexity: O(1) beyond the answer.

Why This Approach Is Not Efficient

With 10^5 indexes and 10^4 updates that each cover the whole array, that's 10^9 additions (over the time limit in Python and javascript).

Inside a range, an update adds the same amount to every index, so the difference between neighbours doesn't change there. It changes in just two places: at start, where the values jump up by inc, and just after end, where they drop back.

Optimal Approach - Difference Array

Intuition

Keep diff, how much each value differs from the one before it. The running total of diff rebuilds the array. An update then needs two writes: diff[start] += inc and diff[end + 1] -= inc. The running total picks inc up at start, carries it through end, and drops it right after.

Give diff one spare slot at the end, so end + 1 is a valid index even when a range reaches the last value. Apply every update, then take the running total once.

Step-by-Step Explanation

For length = 5 and updates = [[1, 3, 2], [2, 4, 3], [0, 2, -2]]:

  1. Each update writes twice into diff. Nothing else is touched until the end.
  2. Update [1, 3, 2]. diff[1] gets 2 where the range starts, and diff[4] gets −2 just after it ends.
  3. Update [2, 4, 3]. diff[2] gets 3 where the range starts, and diff[5] gets −3 just after it ends.
  4. Update [0, 2, −2]. diff[0] gets −2 where the range starts, and diff[3] gets 2 just after it ends.
  5. The running total adds diff[0] = −2, so result[0] is −2.
  6. The running total adds diff[1] = 2, so result[1] is 0.
  7. The running total adds diff[2] = 3, so result[2] is 3.
  8. The running total adds diff[3] = 2, so result[3] is 5.
  9. The running total adds diff[4] = −2, so result[4] is 3.
  10. 6 writes and one pass of 5, however long the ranges are.

Two Writes per Update, One Running Total — Top: diff, with its spare slot at the end. Bottom: the result, filled by the running total of diff.

Algorithm

  1. Make diff, length + 1 zeros.
  2. For each update, diff[start] += inc and diff[end + 1] -= inc.
  3. Keep a running total of diff[0..length-1]; each running total is the next value of the result.

Code

class Solution {
public:
    vector<int> getModifiedArray(int length, vector<vector<int>>& updates) {
        vector<int> diff(length + 1, 0);        // one spare slot, so end + 1 is always a valid index
        for (const auto& u : updates) {
            diff[u[0]] += u[2];                 // the range starts here
            diff[u[1] + 1] -= u[2];             // and stops just after its last index
        }
        vector<int> result(length);
        int running = 0;
        for (int i = 0; i < length; i++) {
            running += diff[i];                 // the running total of the differences
            result[i] = running;
        }
        return result;
    }
};
class Solution:
    def getModifiedArray(self, length: int, updates: list[list[int]]) -> list[int]:
        diff = [0] * (length + 1)               # one spare slot, so end + 1 is always a valid index
        for start, end, inc in updates:
            diff[start] += inc                  # the range starts here
            diff[end + 1] -= inc                # and stops just after its last index
        result, running = [], 0
        for i in range(length):
            running += diff[i]                  # the running total of the differences
            result.append(running)
        return result
class Solution {
    public int[] getModifiedArray(int length, int[][] updates) {
        int[] diff = new int[length + 1];       // one spare slot, so end + 1 is always a valid index
        for (int[] u : updates) {
            diff[u[0]] += u[2];                 // the range starts here
            diff[u[1] + 1] -= u[2];             // and stops just after its last index
        }
        int[] result = new int[length];
        int running = 0;
        for (int i = 0; i < length; i++) {
            running += diff[i];                 // the running total of the differences
            result[i] = running;
        }
        return result;
    }
}
var getModifiedArray = function(length, updates) {
    const diff = new Array(length + 1).fill(0); // one spare slot, so end + 1 is always a valid index
    for (const [start, end, inc] of updates) {
        diff[start] += inc;                     // the range starts here
        diff[end + 1] -= inc;                   // and stops just after its last index
    }
    const result = [];
    let running = 0;
    for (let i = 0; i < length; i++) {
        running += diff[i];                     // the running total of the differences
        result.push(running);
    }
    return result;
};

Complexity Analysis

Time Complexity: O(n + q)

Two writes per update, then one pass.

Space Complexity: O(n) for diff.

Common Mistakes

Each of these looks right on a quick read, and the tests still reject it. The outputs below come from running the code.

Taking inc off at end instead of after it. diff[end] -= inc

On length = 5, updates = [[1, 3, 2], [2, 4, 3], [0, 2, -2]], it returns [-2,0,5,3,0]; the answer is [-2,0,3,5,3]. end is the last index that gets inc, so the running total has to carry it through end and drop it one index later. Writing -inc at end itself cancels it one index too early: the last index of every range misses its update.

No spare slot at the end. diff = [0] * length

On length = 4, updates = [[3, 3, 7]], it crashes with IndexError: list index out of range; the answer is [0,0,0,7]. With exactly length slots, a range that ends on the last index writes -inc to diff[length], one past the end, and crashes. One spare slot at the end takes that write; it's never read into the answer.

Never taking inc back off. diff[start] += inc # and nothing else

On length = 5, updates = [[1, 2, 7]], it returns [0,7,7,7,7]; the answer is [0,7,7,0,0]. +inc at start alone makes the running total carry inc to the end of the array, not to the end of the range. The -inc just after end is what stops it.

Where This Pattern Is Taught

This problem practises the difference array: range updates as two writes and one running total. It's taught in Difference Arrays and 2D Prefix Sums.