You are given an integer array nums
. The range of a subarray of nums
is the difference between the largest and smallest element in the subarray.
Return the sum of all subarray ranges of nums
.
A subarray is a contiguous non-empty sequence of elements within an array.
Example 1:
Input: nums = [1,2,3] Output: 4 Explanation: The 6 subarrays of nums are the following: [1], range = largest - smallest = 1 - 1 = 0 [2], range = 2 - 2 = 0 [3], range = 3 - 3 = 0 [1,2], range = 2 - 1 = 1 [2,3], range = 3 - 2 = 1 [1,2,3], range = 3 - 1 = 2 So the sum of all ranges is 0 + 0 + 0 + 1 + 1 + 2 = 4.
Example 2:
Input: nums = [1,3,3] Output: 4 Explanation: The 6 subarrays of nums are the following: [1], range = largest - smallest = 1 - 1 = 0 [3], range = 3 - 3 = 0 [3], range = 3 - 3 = 0 [1,3], range = 3 - 1 = 2 [3,3], range = 3 - 3 = 0 [1,3,3], range = 3 - 1 = 2 So the sum of all ranges is 0 + 0 + 0 + 2 + 0 + 2 = 4.
Example 3:
Input: nums = [4,-2,-3,4,1] Output: 59 Explanation: The sum of all subarray ranges of nums is 59.
Constraints:
1 <= nums.length <= 1000
-109 <= nums[i] <= 109
Follow-up: Could you find a solution with O(n)
time complexity?
This problem asks to calculate the sum of ranges of all subarrays within a given integer array nums
. The range of a subarray is the difference between its maximum and minimum elements. A brute-force approach would have O(n^3) time complexity, but we can achieve O(n) using a more efficient method.
This approach directly implements the problem description. We iterate through all possible subarrays and calculate their ranges.
Code (Python):
class Solution:
def subArrayRanges(self, nums: List[int]) -> int:
ans, n = 0, len(nums)
for i in range(n):
for j in range(i, n):
sub_array = nums[i:j+1]
ans += max(sub_array) - min(sub_array)
return ans
Time Complexity: O(n^3). The nested loops iterate through all subarrays, and finding the max and min within each subarray takes O(k) time for a subarray of length k. The sum of lengths of all subarrays is O(n^2), leading to overall O(n^3).
Space Complexity: O(1). We only use a few variables to store the current values.
This approach leverages the concept of monotonic stacks to find the maximum and minimum elements efficiently for each element. Instead of recalculating the max and min for every subarray, we keep track of the range contribution each element makes.
Intuition: For each element, we need to know how many times it acts as the maximum (or minimum) within a subarray. We can find this using monotonic stacks that store indices.
Algorithm:
x
is popped, it means the current element is greater, making it the maximum in all subarrays containing x
as well.Code (Python):
class Solution:
def subArrayRanges(self, nums: List[int]) -> int:
def calculate_contribution(nums, is_max):
n = len(nums)
stack = []
contribution = [0] * n
for i, num in enumerate(nums):
while stack and nums[stack[-1]] < num if is_max else nums[stack[-1]] > num:
j = stack.pop()
k = stack[-1] if stack else -1
contribution[j] += (i - j) * (j - k) if is_max else (j - k) * (i - j)
stack.append(i)
while stack:
j = stack.pop()
k = stack[-1] if stack else -1
contribution[j] += (n - j) * (j - k) if is_max else (j - k) * (n - j)
return contribution
max_contribution = calculate_contribution(nums, True)
min_contribution = calculate_contribution(nums, False)
ans = 0
for i in range(len(nums)):
ans += max_contribution[i] * nums[i] - min_contribution[i] * nums[i]
return ans
Time Complexity: O(n). Each element is pushed and popped at most once from the stacks.
Space Complexity: O(n) for the stacks and contribution arrays.
The optimized approach (Approach 2) is significantly faster for larger input arrays because it avoids the nested loop structure of the brute-force approach. Therefore, it's the preferred solution. The code provided in Approach 2 is more complex but achieves the desired linear time complexity. The other language implementations (Java, C++, Go, TypeScript, Rust) would follow a similar logic using their respective stack and array structures.