Problem Statement
Given an integer array nums
, determine the range of a subarray, defined as the difference between the largest and smallest elements within the subarray. Calculate and return the sum of all subarray ranges of nums
.
A subarray is defined as a contiguous, non-empty sequence of elements within the array.
Examples
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 + 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.
Different Approaches
1️⃣ Brute Force Approach
Intuition:
The brute force way to solve this problem is by generating all the subarrays and finding the minimum and maximum values in all of them. The range of a subarray can be found as the difference between the maximum and minimum value which can be summed up for all the subarrays to get the result.
Approach:
- Initialize a variable to store the required sum with 0 initially.
- Traverse on the array. Initialize two variables to store the minimum and maximum elements in the subarray.
- Use a nested loop to traverse all the subarrays and find the minimum and maximum of each subarray. Update the sum for each subarray by finding the range of subarray (maximum-minimum).
- Once all the subarrays are taken into consideration, the result will be stored in the sum variable.
Code:
class Solution {
public:
long long subArrayRanges(vector<int> &nums) {
long long sum = 0;
// Outer loop: iterate over all starting points of subarrays
for (int i = 0; i < nums.size(); i++) {
int mini = INT_MAX; // initialize minimum for the current subarray
int maxi = INT_MIN; // initialize maximum for the current subarray
// Inner loop: extend the subarray from i to j
for (int j = i; j < nums.size(); j++) {
// Update the minimum and maximum values in the current subarray [i..j]
mini = min(mini, nums[j]);
maxi = max(maxi, nums[j]);
// The range of current subarray is max - min
// Add this range to the total sum
sum = sum + (maxi - mini);
}
}
// Return the total of all subarray ranges
return sum;
}
};
Complexity Analysis:
- Time Complexity:
O(N^2)
(where N is the size of the array)- Using two nested loops.
- Space Complexity:
O(1)
- Using only a couple of variables.
2️⃣ Monotonic Stack
To optimally find the sum of largest and the smallest elements in the array, the concept of Next/Previous Smaller/Greater Elements come into picture as discussed in the Sum of Subarray minimums (Optimal Solution).
Approach:
- Four helper functions are used to find specific indices:
- Next Smaller Elements (NSE): For each element, find the index of the next smaller element to its right.
- Next Greater Elements (NGE): For each element, find the index of the next greater element to its right.
- Previous Smaller or Equal Elements (PSEE): For each element, find the index of the previous smaller or equal element to its left.
- Previous Greater or Equal Elements (PGEE): For each element, find the index of the previous greater or equal element to its left.
- Utilize the NSE and PSEE indices to calculate the sum of the minimum values in all subarrays. Utilize the NGE and PGEE indices to calculate the sum of the maximum values in all subarrays.
- The final result is obtained by subtracting the sum of subarray minimums from the sum of subarray maximums. This gives the sum of ranges of all subarrays.
Code:
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
/* Function to find the indices of
next smaller elements */
vector<int> findNSE(vector<int> &arr) {
// Size of array
int n = arr.size();
// To store the answer
vector<int> ans(n);
// Stack
stack<int> st;
// Start traversing from the back
for(int i = n - 1; i >= 0; i--) {
// Get the current element
int currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
element is not the smaller element */
while(!st.empty() && arr[st.top()] >= currEle){
st.pop();
}
// Update the answer
ans[i] = !st.empty() ? st.top() : n;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
/* Function to find the indices of
next greater elements */
vector<int> findNGE(vector<int> &arr) {
// Size of array
int n = arr.size();
// To store the answer
vector<int> ans(n);
// Stack
stack<int> st;
// Start traversing from the back
for(int i = n - 1; i >= 0; i--) {
// Get the current element
int currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
element is not the greater element */
while(!st.empty() && arr[st.top()] <= currEle){
st.pop();
}
// Update the answer
ans[i] = !st.empty() ? st.top() : n;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
/* Function to find the indices of
previous smaller or equal elements */
vector<int> findPSEE(vector<int> &arr) {
// Size of array
int n = arr.size();
// To store the answer
vector<int> ans(n);
// Stack
stack<int> st;
// Traverse on the array
for(int i=0; i < n; i++) {
// Get the current element
int currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
elements are greater than the current element */
while(!st.empty() && arr[st.top()] > currEle){
st.pop();
}
// Update the answer
ans[i] = !st.empty() ? st.top() : -1;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
/* Function to find the indices of
previous greater or equal elements */
vector<int> findPGEE(vector<int> &arr) {
// Size of array
int n = arr.size();
// To store the answer
vector<int> ans(n);
// Stack
stack<int> st;
// Traverse on the array
for(int i=0; i < n; i++) {
// Get the current element
int currEle = arr[i];
/* Pop the elements in the stack until
the stack is not empty and the top
elements are smaller than the current element */
while(!st.empty() && arr[st.top()] < currEle){
st.pop();
}
// Update the answer
ans[i] = !st.empty() ? st.top() : -1;
/* Push the index of current
element in the stack */
st.push(i);
}
// Return the answer
return ans;
}
/* Function to find the sum of the
minimum value in each subarray */
long long sumSubarrayMins(vector<int> &arr) {
vector<int> nse = findNSE(arr);
vector<int> psee = findPSEE(arr);
// Size of array
int n = arr.size();
// To store the sum
long long sum = 0;
// Traverse on the array
for(int i=0; i < n; i++) {
// Count of first type of subarrays
int left = i - psee[i];
// Count of second type of subarrays
int right = nse[i] - i;
/* Count of subarrays where
current element is minimum */
long long freq = left*right*1LL;
// Contribution due to current element
long long val = (freq*arr[i]*1LL);
// Updating the sum
sum += val;
}
// Return the computed sum
return sum;
}
/* Function to find the sum of the
maximum value in each subarray */
long long sumSubarrayMaxs(vector<int> &arr) {
vector<int> nge = findNGE(arr);
vector<int> pgee = findPGEE(arr);
// Size of array
int n = arr.size();
// To store the sum
long long sum = 0;
// Traverse on the array
for(int i=0; i < n; i++) {
// Count of first type of subarrays
int left = i - pgee[i];
// Count of second type of subarrays
int right = nge[i] - i;
/* Count of subarrays where
current element is minimum */
long long freq = left*right*1LL;
// Contribution due to current element
long long val = (freq*arr[i]*1LL);
// Updating the sum
sum += val;
}
// Return the computed sum
return sum;
}
public:
/* Function to find the sum of
subarray ranges in each subarray */
long long subArrayRanges(vector<int> &arr) {
// Return the result
return ( sumSubarrayMaxs(arr) -
sumSubarrayMins(arr) );
}
};
int main() {
vector<int> arr = {1, 2, 3};
/* Creating an instance of
Solution class */
Solution sol;
/* Function call to find the sum of
subarray ranges in each subarray */
long long ans = sol.subArrayRanges(arr);
cout << "The sum of subarray ranges is: " << ans;
return 0;
}
Complexity Analysis:
- Time Complexity:
O(N)
(where N is the size of given array)- Calculating the sum of subarray maximums takes O(N) time.
- Calculating the sum of subarray minimums takes O(N) time.
- Space Complexity:
O(N)
- Calculating the sum of subarray maximums requires O(N) space.
- Calculating the sum of subarray minimums requires O(N) space.