2104. Sum of Subarray Ranges
Problem Description
You are given an integer array nums
. For each subarray in nums
, you need to find its range, which is defined as the difference between the maximum element and minimum element in that subarray.
Your task is to calculate the sum of ranges of all possible subarrays and return this total sum.
A subarray is a contiguous sequence of one or more elements from the original array. For example, if nums = [1, 2, 3]
, the subarrays are: [1]
, [2]
, [3]
, [1,2]
, [2,3]
, and [1,2,3]
.
For each subarray:
- Find the maximum element
- Find the minimum element
- Calculate the range as
max - min
- Add this range to the running sum
The solution uses a nested loop approach where for each starting position i
, it considers all subarrays starting at i
and ending at positions j
(where j >= i
). As it extends each subarray, it tracks the running minimum and maximum values, calculates the range, and adds it to the total sum.
For example, with nums = [1, 3, 2]
:
- Subarray
[1]
: range =1 - 1 = 0
- Subarray
[3]
: range =3 - 3 = 0
- Subarray
[2]
: range =2 - 2 = 0
- Subarray
[1, 3]
: range =3 - 1 = 2
- Subarray
[3, 2]
: range =3 - 2 = 1
- Subarray
[1, 3, 2]
: range =3 - 1 = 2
- Total sum =
0 + 0 + 0 + 2 + 1 + 2 = 5
Intuition
The key insight is that we need to examine every possible subarray and calculate its range. Since a subarray is defined by its starting and ending positions, we can systematically generate all subarrays using two pointers.
The brute force approach is actually quite natural here: for each possible starting position i
, we expand the subarray by moving the ending position j
from i
to the end of the array. This generates all subarrays starting at position i
.
The clever optimization in this solution is that we don't need to recalculate the minimum and maximum from scratch for each subarray. When we extend a subarray from [i...j-1]
to [i...j]
, we only need to:
- Update the minimum:
mi = min(mi, nums[j])
- Update the maximum:
mx = max(mx, nums[j])
This works because adding one more element to a subarray can only potentially change the min/max if the new element is smaller than the current minimum or larger than the current maximum.
For example, if we have subarray [3, 1]
with min=1, max=3
, and we extend it to [3, 1, 5]
, we only need to compare 5
with our current min and max values, rather than scanning through all three elements again.
This incremental update approach reduces redundant calculations. Instead of recalculating min/max for each subarray from scratch (which would take O(n³)
time), we maintain running min/max values as we expand each subarray, achieving O(n²)
time complexity.
The solution iterates through all n*(n+1)/2
possible subarrays exactly once, computing each range in constant time using the maintained min/max values, and accumulating the sum of all ranges.
Learn more about Stack and Monotonic Stack patterns.
Solution Approach
The implementation uses a nested loop structure to enumerate all possible subarrays and calculate their ranges efficiently.
Algorithm Steps:
-
Initialize variables: Set
ans = 0
to store the sum of all ranges, and get the array lengthn
. -
Outer loop (
i
from0
ton-2
): This loop sets the starting position of each subarray. We stop atn-2
because we need at least one more element to form a subarray with the next position. -
Initialize min/max trackers: For each starting position
i
, initialize bothmi
(minimum) andmx
(maximum) tonums[i]
. These will track the minimum and maximum values in the current subarray as we expand it. -
Inner loop (
j
fromi+1
ton-1
): This loop extends the subarray from positioni
to positionj
.- Update the minimum:
mi = min(mi, nums[j])
- Update the maximum:
mx = max(mx, nums[j])
- Calculate the range:
mx - mi
- Add the range to the total sum:
ans += mx - mi
- Update the minimum:
-
Return the result: After processing all subarrays, return
ans
.
Key Implementation Details:
- The outer loop starts at each possible subarray starting position
- The inner loop extends the subarray one element at a time
- By maintaining running
mi
andmx
values, we avoid recalculating the min/max for the entire subarray each time - Note that single-element subarrays (where
i == j
) have a range of0
since min equals max. The code handles this implicitly by starting the inner loop ati+1
, and these single-element ranges contribute0
to the sum
Time Complexity: O(n²)
- We have two nested loops, each iterating through the array.
Space Complexity: O(1)
- We only use a constant amount of extra space for variables ans
, mi
, and mx
.
This brute force approach is straightforward and efficient enough for moderate input sizes, as it processes each subarray exactly once with minimal overhead.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [4, 1, 3]
:
Step 1: Initialize
ans = 0
(to accumulate the sum of ranges)n = 3
(length of array)
Step 2: Process subarrays starting at index 0 (i = 0)
- Initialize:
mi = 4
,mx = 4
- Extend to j = 1:
nums[1] = 1
- Update:
mi = min(4, 1) = 1
,mx = max(4, 1) = 4
- Range of
[4, 1]
=4 - 1 = 3
- Add to sum:
ans = 0 + 3 = 3
- Update:
- Extend to j = 2:
nums[2] = 3
- Update:
mi = min(1, 3) = 1
,mx = max(4, 3) = 4
- Range of
[4, 1, 3]
=4 - 1 = 3
- Add to sum:
ans = 3 + 3 = 6
- Update:
Step 3: Process subarrays starting at index 1 (i = 1)
- Initialize:
mi = 1
,mx = 1
- Extend to j = 2:
nums[2] = 3
- Update:
mi = min(1, 3) = 1
,mx = max(1, 3) = 3
- Range of
[1, 3]
=3 - 1 = 2
- Add to sum:
ans = 6 + 2 = 8
- Update:
Step 4: No more starting positions (i = 2 would be the last element)
Result: Total sum = 8
Note: Single-element subarrays [4]
, [1]
, [3]
each have range = 0 (max - min = 0), so they don't contribute to the sum. The algorithm implicitly handles these by starting the inner loop at i+1
.
The key efficiency comes from maintaining running min/max values as we extend each subarray, rather than recalculating them from scratch each time.
Solution Implementation
1class Solution:
2 def subArrayRanges(self, nums: List[int]) -> int:
3 """
4 Calculate the sum of ranges for all subarrays.
5 Range of a subarray = max(subarray) - min(subarray)
6
7 Args:
8 nums: List of integers
9
10 Returns:
11 Sum of all subarray ranges
12 """
13 total_range_sum = 0
14 array_length = len(nums)
15
16 # Iterate through all possible starting positions of subarrays
17 for start_index in range(array_length - 1):
18 # Initialize min and max with the starting element
19 current_min = nums[start_index]
20 current_max = nums[start_index]
21
22 # Extend the subarray by including elements one by one
23 for end_index in range(start_index + 1, array_length):
24 # Update min and max values for current subarray
25 current_min = min(current_min, nums[end_index])
26 current_max = max(current_max, nums[end_index])
27
28 # Add the range of current subarray to total sum
29 # Range = max - min for subarray from start_index to end_index
30 total_range_sum += current_max - current_min
31
32 return total_range_sum
33
1class Solution {
2 /**
3 * Calculates the sum of all subarray ranges.
4 * Range of a subarray is defined as the difference between its maximum and minimum elements.
5 *
6 * @param nums the input array of integers
7 * @return the sum of ranges for all possible subarrays
8 */
9 public long subArrayRanges(int[] nums) {
10 long totalSum = 0;
11 int arrayLength = nums.length;
12
13 // Iterate through each possible starting position of subarrays
14 for (int startIndex = 0; startIndex < arrayLength - 1; startIndex++) {
15 // Initialize min and max with the current starting element
16 int minValue = nums[startIndex];
17 int maxValue = nums[startIndex];
18
19 // Extend the subarray by including elements one by one
20 for (int endIndex = startIndex + 1; endIndex < arrayLength; endIndex++) {
21 // Update min and max values for the current subarray
22 minValue = Math.min(minValue, nums[endIndex]);
23 maxValue = Math.max(maxValue, nums[endIndex]);
24
25 // Add the range (max - min) of current subarray to the total sum
26 totalSum += (maxValue - minValue);
27 }
28 }
29
30 return totalSum;
31 }
32}
33
1class Solution {
2public:
3 long long subArrayRanges(vector<int>& nums) {
4 long long totalSum = 0;
5 int arraySize = nums.size();
6
7 // Iterate through each possible starting position of subarrays
8 for (int startIdx = 0; startIdx < arraySize - 1; ++startIdx) {
9 // Initialize min and max values with the current element
10 int minValue = nums[startIdx];
11 int maxValue = nums[startIdx];
12
13 // Extend the subarray by including elements from startIdx+1 to end
14 for (int endIdx = startIdx + 1; endIdx < arraySize; ++endIdx) {
15 // Update the minimum value in current subarray
16 minValue = min(minValue, nums[endIdx]);
17 // Update the maximum value in current subarray
18 maxValue = max(maxValue, nums[endIdx]);
19
20 // Add the range (max - min) of current subarray to total sum
21 totalSum += (maxValue - minValue);
22 }
23 }
24
25 return totalSum;
26 }
27};
28
1/**
2 * Calculates the sum of all subarray ranges in the given array.
3 * A subarray range is defined as the difference between the maximum and minimum elements in a subarray.
4 *
5 * @param nums - The input array of numbers
6 * @returns The sum of all subarray ranges
7 */
8function subArrayRanges(nums: number[]): number {
9 const arrayLength: number = nums.length;
10 let totalRangeSum: number = 0;
11
12 // Iterate through each possible starting position of subarrays
13 for (let startIndex: number = 0; startIndex < arrayLength - 1; startIndex++) {
14 // Initialize min and max with the current starting element
15 let currentMin: number = nums[startIndex];
16 let currentMax: number = nums[startIndex];
17
18 // Extend the subarray by including elements from startIndex+1 to end
19 for (let endIndex: number = startIndex + 1; endIndex < arrayLength; endIndex++) {
20 // Update the minimum value in the current subarray
21 currentMin = Math.min(currentMin, nums[endIndex]);
22 // Update the maximum value in the current subarray
23 currentMax = Math.max(currentMax, nums[endIndex]);
24
25 // Add the range (max - min) of current subarray to the total sum
26 totalRangeSum += currentMax - currentMin;
27 }
28 }
29
30 return totalRangeSum;
31}
32
Time and Space Complexity
Time Complexity: O(n²)
The code uses two nested loops:
- The outer loop runs from
i = 0
toi = n-2
, which executesn-1
times - For each iteration
i
, the inner loop runs fromj = i+1
toj = n-1
, which executesn-i-1
times
The total number of iterations is:
(n-1) + (n-2) + (n-3) + ... + 1 = n(n-1)/2
Inside the inner loop, we perform constant time operations (min()
, max()
, and addition), each taking O(1)
time.
Therefore, the overall time complexity is O(n²)
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space:
ans
: stores the running sumn
: stores the length of the arrayi
,j
: loop countersmi
,mx
: track minimum and maximum values for current subarray
All these variables use O(1)
space regardless of the input size, making the space complexity O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Missing Single-Element Subarrays
The Pitfall: The current implementation starts the inner loop at start_index + 1
, which means it never explicitly processes single-element subarrays. While this happens to work correctly (since single-element subarrays have a range of 0 and don't contribute to the sum), it can lead to confusion and potential bugs if the problem requirements change.
Why It Happens: Developers might assume all subarrays are being processed, but the code structure skips the case where start_index == end_index
.
Solution: Make the code more explicit and complete by handling all subarrays:
def subArrayRanges(self, nums: List[int]) -> int:
total_range_sum = 0
array_length = len(nums)
# Process ALL subarrays, including single elements
for start_index in range(array_length):
current_min = nums[start_index]
current_max = nums[start_index]
# Start from start_index (not start_index + 1)
for end_index in range(start_index, array_length):
current_min = min(current_min, nums[end_index])
current_max = max(current_max, nums[end_index])
total_range_sum += current_max - current_min
return total_range_sum
2. Integer Overflow in Other Languages
The Pitfall: When implementing this solution in languages like C++ or Java, the sum might exceed the integer limit for large arrays with large values.
Why It Happens: With n elements, there are n(n+1)/2 subarrays. If n=1000 and ranges are large, the sum can exceed 32-bit integer limits.
Solution: Use appropriate data types:
- In Python: This isn't an issue due to arbitrary precision integers
- In Java: Use
long
instead ofint
for the sum - In C++: Use
long long
for the accumulator
3. Off-by-One Error in Loop Bounds
The Pitfall: Using range(array_length - 1)
for the outer loop when you actually want to process all starting positions.
Why It Happens: Confusion about whether single-element subarrays should be included, or misunderstanding that the last element can also be a starting position.
Solution: Carefully consider the loop bounds:
- If including single-element subarrays: outer loop should be
range(array_length)
- If excluding them (as in original): outer loop
range(array_length - 1)
is correct but document this choice clearly
4. Inefficient Min/Max Recalculation
The Pitfall: Some might be tempted to recalculate min and max for the entire subarray in each iteration:
# INEFFICIENT - Don't do this!
for start_index in range(array_length):
for end_index in range(start_index, array_length):
subarray = nums[start_index:end_index+1]
total_range_sum += max(subarray) - min(subarray)
Why It Happens: It seems more straightforward to work with the actual subarray.
Solution: Always maintain running min/max values as shown in the original solution. This reduces time complexity from O(n³) to O(n²).
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Monotonic Stack Deque Intro If you'd prefer a video div class responsive iframe iframe src https www youtube com embed Dq_ObZwTY_Q title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div The word monotonic means a list or
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!