Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
From Brute Force to Optimal: Subarray Ranges Explained via Monotonic Stacks 🔥
May 17, 2025
456 views
Written by Prashant Basnet
👋 Welcome to my Signature, a space between logic and curiosity.
I’m a Software Development Engineer who loves turning ideas into systems that work beautifully.
This space captures the process: the bugs, breakthroughs, and “aha” moments that keep me building.
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:
Example 2:
Example 3:
Situation:
Task:
Action:
🛠️ Brute Force Approach:
const subarrays = [] for (let i = 0; i < nums.length; i++) { for (let j = i; j < nums.length; j++) { subarrays.push(nums.slice(i, j + 1)) } } let sum = 0 for (let subarray of subarrays) { let min = Math.min(...subarray) let max = Math.max(...subarray) sum += max - min } return sum⚡ Optimization Insight
Optimized Solution O(n) Using Monotonic Stacks:
Instead of computing the range (max - min) for each subarray, flip the approach:
Use these to compute total contribution of each element to the final sum. Each element contributes to the total as:
We can use monotonic stack
for (let i = 0; i < n; i++) {1. For next smaller elements
while (0 < stack.length && nums[i] <= nums[stack[stack.length - 1]]) { stack.pop() } prev[i] = stack.length === 0 ? -1 : stack[stack.length - 1] stack.push(i) }2. For prev smaller elements
for (let i = n - 1; -1 < i; i--) { while (0 < stack.length && nums[i] < nums[stack[stack.length - 1]]) { stack.pop() } next[i] = stack.length === 0 ? n : stack[stack.length - 1] stack.push(i) }let minSum = 0 for(let i = 0 ; i < n; i++){ let left = i - prev[i] let right = next[i] - i minSum += nums[i] * left * right }3. Now finding the maxSum
4. For next greater elements
for (let i = 0; i < n; i++) { while (0 < stack.length && nums[stack[stack.length - 1]] <= nums[i]) { stack.pop() } prev[i] = stack.length === 0 ? -1 : stack[stack.length - 1] stack.push(i) }5. For prev greater elements
for (let i = n - 1; -1 < i; i--) { while (0 < stack.length && nums[stack[stack.length - 1]] < nums[i]) { stack.pop() } next[i] = stack.length === 0 ? n : stack[stack.length - 1] stack.push(i) }for (let i = 0; i < n; i++) { let left = i - prev[i] let right = next[i] - i maxSum += nums[i] * left * right }🔚 Conclusion:
Instead of brute-forcing all subarrays, we flipped the problem:
Using monotonic stacks, we tracked that efficiently and summed their contributions.
🧠 Master this pattern and you'll unlock a whole new level in:
Next time you're stuck with O(n²), think: ➡️ "Can I reframe it in terms of element contribution?