303. Range Sum Query - Immutable - Explanation
Description
You are given an integer array nums, handle multiple queries of the following type:
- Calculate the sum of the elements of
numsbetween indicesleftandrightinclusive whereleft <= right.
Implement the NumArray class:
NumArray(int[] nums)Initializes the object with the integer arraynums.int sumRange(int left, int right)Returns the sum of the elements ofnumsbetween indicesleftandrightinclusive (i.e.nums[left] + nums[left + 1] + ... + nums[right]).
Example 1:
Input: ["NumArray","sumRange","sumRange","sumRange"]
[[[-2,0,3,-5,2,-1]],[0,2],[2,5],[0,5]]
Output: [null,1,-1,-3]Explanation: NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return (-2) + 0 + 3 = 1
numArray.sumRange(2, 5); // return 3 + (-5) + 2 + (-1) = -1
numArray.sumRange(0, 5); // return (-2) + 0 + 3 + (-5) + 2 + (-1) = -3
Constraints:
1 <= nums.length <= 10,000-100,000 <= nums[i] <= 100,0000 <= left <= right < nums.length- At most 10,000 calls will be made to
sumRange.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Arrays - Basic array traversal and element access for computing range sums
- Prefix Sums - Precomputing cumulative sums to enable constant-time range queries
- Segment Trees (Optional) - Alternative data structure for range queries, though overkill for immutable arrays
1. Brute Force
Intuition
The simplest approach is to iterate through the array from left to right and add up all the elements. This works correctly but is inefficient when we need to answer many queries, since each query requires scanning through the entire range.
Algorithm
- Store the original array.
- For each
sumRange(left, right)query:- Initialize
res = 0. - Loop through indices from
lefttoright. - Add each element to
res.
- Initialize
- Return
res.
Time & Space Complexity
- Time complexity: for each query.
- Space complexity: since we only make a reference to the input array.
2. Prefix Sum - I
Intuition
We can precompute a prefix sum array where prefix[i] stores the sum of all elements from index 0 to i. To find the sum of any range [left, right], we take prefix[right] and subtract prefix[left - 1] (if left > 0). This gives us constant-time queries after a linear-time preprocessing step.
Algorithm
- Build a prefix sum array during construction:
- Maintain a running sum
cur. - For each element, add it to
curand store inprefix.
- Maintain a running sum
- For each
sumRange(left, right)query:- Get
rightSum = prefix[right]. - If
left > 0, setleftSum = prefix[left - 1], otherwiseleftSum = 0. - Return
rightSum - leftSum.
- Get
class NumArray:
def __init__(self, nums):
self.prefix = []
cur = 0
for num in nums:
cur += num
self.prefix.append(cur)
def sumRange(self, left, right):
rightSum = self.prefix[right]
leftSum = self.prefix[left - 1] if left > 0 else 0
return rightSum - leftSumTime & Space Complexity
- Time complexity: for each query, for building the prefix sum array.
- Space complexity:
3. Prefix Sum - II
Intuition
This is a cleaner variation of the prefix sum approach. By creating an array of size n + 1 where prefix[0] = 0, we avoid the edge case when left = 0. The value prefix[i + 1] represents the sum of the first i + 1 elements. The range sum becomes simply prefix[right + 1] - prefix[left].
Algorithm
- Create a
prefixarray of sizen + 1initialized to0. - Build the prefix sums:
- For each index
i, setprefix[i + 1] = prefix[i] + nums[i].
- For each index
- For each
sumRange(left, right)query:- Return
prefix[right + 1] - prefix[left].
- Return
Time & Space Complexity
- Time complexity: for each query, for building the prefix sum array.
- Space complexity:
4. Segment Tree
Intuition
A segment tree is a binary tree where each node stores the sum of a range of elements. The root contains the sum of the entire array, and each leaf contains a single element. While this is overkill for an immutable array (prefix sums are simpler and faster), segment trees become essential when updates are needed. For this immutable version, queries still run in logarithmic time.
Algorithm
- Build the segment tree:
- Store leaf values at indices
nto2n - 1. - Compute internal nodes bottom-up:
tree[i] = tree[2*i] + tree[2*i + 1].
- Store leaf values at indices
- For each
sumRange(left, right)query:- Shift
leftandrightto leaf positions. - While
left < right:- If
leftis odd, addtree[left]to the result and incrementleft. - If
rightis odd, decrementrightand addtree[right]to the result. - Move both pointers to their parents.
- If
- Shift
- Return the accumulated sum.
class SegmentTree:
def __init__(self, nums):
self.n = len(nums)
self.tree = [0] * (2 * self.n)
for i in range(self.n):
self.tree[self.n + i] = nums[i]
for i in range(self.n - 1, 0, -1):
self.tree[i] = self.tree[2 * i] + self.tree[2 * i + 1]
def query(self, left, right):
left += self.n
right += self.n + 1
res = 0
while left < right:
if left % 2 == 1:
res += self.tree[left]
left += 1
if right % 2 == 1:
right -= 1
res += self.tree[right]
left //= 2
right //= 2
return res
class NumArray:
def __init__(self, nums):
self.segTree = SegmentTree(nums)
def sumRange(self, left, right):
return self.segTree.query(left, right)Time & Space Complexity
- Time complexity: for each query, for building the Segment Tree.
- Space complexity:
Common Pitfalls
Off-by-One Error When Left Index Is Zero
When using a prefix sum array where prefix[i] represents the sum from index 0 to i, computing sumRange(0, right) requires special handling. Attempting to access prefix[-1] causes an error. The fix is either to check if left > 0 before subtracting, or use a prefix array of size n + 1 where prefix[0] = 0 so that prefix[right + 1] - prefix[left] always works without edge cases.
Modifying the Original Array After Preprocessing
Since prefix sums are precomputed during initialization, any subsequent changes to the original array will not be reflected in query results. This approach only works for immutable arrays. If updates are needed, a different data structure like a segment tree or Fenwick tree must be used instead of static prefix sums.
Sign in to join the discussion