2908. Minimum Sum of Mountain Triplets I
Problem Description
You are given a 0-indexed array nums
of integers.
A triplet of indices (i, j, k)
forms a mountain if:
- The indices are in strictly increasing order:
i < j < k
- The middle element is the peak:
nums[i] < nums[j]
andnums[k] < nums[j]
In other words, nums[j]
must be greater than both nums[i]
and nums[k]
, creating a mountain-like pattern where the value rises from position i
to j
, then falls from j
to k
.
Your task is to find the minimum possible sum of any mountain triplet in the array. The sum of a mountain triplet (i, j, k)
is calculated as nums[i] + nums[j] + nums[k]
.
If no valid mountain triplet exists in the array, return -1
.
For example:
- If
nums = [8, 6, 1, 5, 3]
, a valid mountain triplet is(2, 3, 4)
with values(1, 5, 3)
, where1 < 5 > 3
. The sum would be1 + 5 + 3 = 9
. - If
nums = [5, 4, 3, 2, 1]
, no mountain triplet exists since the array is strictly decreasing.
Intuition
To find the minimum sum of a mountain triplet, we need three elements where the middle one is the peak. The key insight is that for each potential peak element, we want to find the smallest element on its left and the smallest element on its right.
Think about it this way: if we fix the middle element nums[j]
as our peak, then to minimize the total sum, we need:
- The smallest possible
nums[i]
wherei < j
andnums[i] < nums[j]
- The smallest possible
nums[k]
wherek > j
andnums[k] < nums[j]
A brute force approach would check all possible triplets, which takes O(n³)
time. But we can optimize this by recognizing that for each potential peak position, we don't need to check all elements - we just need the minimum values on each side.
The challenge is efficiently finding these minimum values. For the left side, we can maintain a running minimum as we traverse the array. But for the right side, we need to know the minimum value after each position before we start our main traversal.
This leads us to a two-pass approach:
-
First pass (right to left): Precompute the minimum value to the right of each position. Store this in an array
right[i]
whereright[i]
represents the minimum value innums[i+1..n-1]
. -
Second pass (left to right): For each position
j
, check if it can be a valid peak by comparing it with the minimum on its left (maintained as we go) and the minimum on its right (from our precomputed array). Ifleft_min < nums[j] > right_min
, we have a valid mountain, and we can calculate its sum.
This approach reduces the time complexity to O(n)
with O(n)
extra space, making it much more efficient than checking all triplets.
Solution Approach
Let's implement the solution step by step based on our intuition:
Step 1: Preprocess right minimums
First, we create an array right
of size n+1
to store the minimum values to the right of each position. We initialize it with infinity values.
right = [inf] * (n + 1)
for i in range(n - 1, -1, -1):
right[i] = min(right[i + 1], nums[i])
We traverse from right to left (indices n-1
to 0
). For each position i
, right[i]
stores the minimum of:
right[i + 1]
: the minimum value to the right of positioni+1
nums[i]
: the current element
This way, right[i]
contains the minimum value in nums[i..n-1]
, and right[i+1]
contains the minimum value in nums[i+1..n-1]
.
Step 2: Find the minimum mountain sum
Now we traverse the array from left to right, treating each element as a potential peak:
ans = left = inf
for i, x in enumerate(nums):
if left < x and right[i + 1] < x:
ans = min(ans, left + x + right[i + 1])
left = min(left, x)
During this traversal:
left
maintains the minimum value seen so far (to the left of current position)x
is the current elementnums[i]
, which we consider as the potential peakright[i + 1]
gives us the minimum value to the right of positioni
For each position i
, we check if it can form a valid mountain:
left < x
: there exists a smaller element on the leftright[i + 1] < x
: there exists a smaller element on the right
If both conditions are satisfied, we have a valid mountain triplet with sum left + x + right[i + 1]
. We update ans
with the minimum sum found so far.
After checking position i
as a peak, we update left
to include the current element for future iterations.
Step 3: Return the result
return -1 if ans == inf else ans
If ans
remains as infinity, it means no valid mountain triplet was found, so we return -1
. Otherwise, we return the minimum sum found.
Time Complexity: O(n)
- two passes through the array
Space Complexity: O(n)
- for the right
array
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 = [8, 6, 1, 5, 3]
:
Step 1: Build the right minimum array
We traverse from right to left to build right[]
:
- Start with
right = [inf, inf, inf, inf, inf, inf]
i=4
:right[4] = min(right[5], nums[4]) = min(inf, 3) = 3
i=3
:right[3] = min(right[4], nums[3]) = min(3, 5) = 3
i=2
:right[2] = min(right[3], nums[2]) = min(3, 1) = 1
i=1
:right[1] = min(right[2], nums[1]) = min(1, 6) = 1
i=0
:right[0] = min(right[1], nums[0]) = min(1, 8) = 1
Final right = [1, 1, 1, 3, 3, inf]
This means:
right[1] = 1
: minimum innums[1..4]
is 1right[2] = 1
: minimum innums[2..4]
is 1right[3] = 3
: minimum innums[3..4]
is 3right[4] = 3
: minimum innums[4..4]
is 3
Step 2: Find minimum mountain sum
Now traverse left to right, checking each position as a potential peak:
-
i=0, nums[0]=8
:left = inf
,right[1] = 1
- Check:
inf < 8
? No, so not a valid peak - Update:
left = min(inf, 8) = 8
-
i=1, nums[1]=6
:left = 8
,right[2] = 1
- Check:
8 < 6
? No, so not a valid peak - Update:
left = min(8, 6) = 6
-
i=2, nums[2]=1
:left = 6
,right[3] = 3
- Check:
6 < 1
? No, so not a valid peak - Update:
left = min(6, 1) = 1
-
i=3, nums[3]=5
:left = 1
,right[4] = 3
- Check:
1 < 5
AND3 < 5
? Yes! Valid mountain - Sum =
1 + 5 + 3 = 9
- Update:
ans = min(inf, 9) = 9
,left = min(1, 5) = 1
-
i=4, nums[4]=3
:left = 1
,right[5] = inf
- Check:
1 < 3
ANDinf < 3
? No, so not a valid peak - Update:
left = min(1, 3) = 1
Step 3: Return result
ans = 9
, so we return 9
.
The mountain triplet found is (2, 3, 4)
with values (1, 5, 3)
, giving the minimum sum of 9.
Solution Implementation
1class Solution:
2 def minimumSum(self, nums: List[int]) -> int:
3 # Get the length of the input array
4 n = len(nums)
5
6 # Build array to track minimum value to the right of each position
7 # min_right[i] stores the minimum value from index i to the end
8 min_right = [float('inf')] * (n + 1)
9
10 # Populate min_right array from right to left
11 for i in range(n - 1, -1, -1):
12 min_right[i] = min(min_right[i + 1], nums[i])
13
14 # Initialize result and left minimum tracker
15 result = float('inf')
16 min_left = float('inf')
17
18 # Iterate through array to find valid triplets
19 for i, current_value in enumerate(nums):
20 # Check if current element can be the middle of a valid triplet
21 # Need: min_left < current_value and min_right[i+1] < current_value
22 if min_left < current_value and min_right[i + 1] < current_value:
23 # Calculate sum of the triplet and update result
24 triplet_sum = min_left + current_value + min_right[i + 1]
25 result = min(result, triplet_sum)
26
27 # Update the minimum value seen so far from the left
28 min_left = min(min_left, current_value)
29
30 # Return -1 if no valid triplet found, otherwise return the minimum sum
31 return -1 if result == float('inf') else result
32
1class Solution {
2 public int minimumSum(int[] nums) {
3 int n = nums.length;
4
5 // Create an array to store the minimum value to the right of each index
6 // rightMin[i] represents the minimum value from index i to the end of the array
7 int[] rightMin = new int[n + 1];
8
9 // Initialize infinity value as a large number (2^30)
10 final int INF = 1 << 30;
11
12 // Set the rightmost boundary to infinity (no element after the last index)
13 rightMin[n] = INF;
14
15 // Build the rightMin array by iterating from right to left
16 // Each position stores the minimum of current element and minimum to its right
17 for (int i = n - 1; i >= 0; i--) {
18 rightMin[i] = Math.min(rightMin[i + 1], nums[i]);
19 }
20
21 // Initialize the answer and left minimum tracker
22 int minSum = INF;
23 int leftMin = INF;
24
25 // Iterate through the array to find valid triplets
26 for (int i = 0; i < n; i++) {
27 // Check if current element can be the middle element of a valid triplet
28 // Condition: leftMin < nums[i] > rightMin[i+1]
29 // This forms a mountain triplet (nums[j] < nums[i] > nums[k])
30 if (leftMin < nums[i] && rightMin[i + 1] < nums[i]) {
31 // Calculate the sum of the triplet and update minimum
32 int currentSum = leftMin + nums[i] + rightMin[i + 1];
33 minSum = Math.min(minSum, currentSum);
34 }
35
36 // Update the minimum value seen so far from the left
37 leftMin = Math.min(leftMin, nums[i]);
38 }
39
40 // Return -1 if no valid triplet found, otherwise return the minimum sum
41 return minSum == INF ? -1 : minSum;
42 }
43}
44
1class Solution {
2public:
3 int minimumSum(vector<int>& nums) {
4 int n = nums.size();
5 const int INF = 1 << 30; // Large value representing infinity
6
7 // Build array to track minimum element to the right of each position
8 int rightMin[n + 1];
9 rightMin[n] = INF; // Initialize boundary with infinity
10
11 // Fill rightMin array from right to left
12 // rightMin[i] = minimum value from nums[i] to nums[n-1]
13 for (int i = n - 1; i >= 0; --i) {
14 rightMin[i] = min(rightMin[i + 1], nums[i]);
15 }
16
17 int result = INF; // Initialize result with infinity
18 int leftMin = INF; // Track minimum element to the left of current position
19
20 // Iterate through array to find valid triplets
21 for (int i = 0; i < n; ++i) {
22 // Check if nums[i] can be the middle element of a valid triplet
23 // Condition: leftMin < nums[i] > rightMin[i+1]
24 if (leftMin < nums[i] && rightMin[i + 1] < nums[i]) {
25 // Update result with sum of the triplet
26 result = min(result, leftMin + nums[i] + rightMin[i + 1]);
27 }
28 // Update leftMin for next iteration
29 leftMin = min(leftMin, nums[i]);
30 }
31
32 // Return -1 if no valid triplet found, otherwise return the minimum sum
33 return result == INF ? -1 : result;
34 }
35};
36
1/**
2 * Finds the minimum sum of three elements where the middle element is greater than both side elements
3 * @param nums - The input array of numbers
4 * @returns The minimum sum of valid triplet, or -1 if no valid triplet exists
5 */
6function minimumSum(nums: number[]): number {
7 const n: number = nums.length;
8
9 // Build array to track minimum value to the right of each position
10 // rightMin[i] stores the minimum value from index i to the end
11 const rightMin: number[] = Array(n + 1).fill(Infinity);
12 for (let i: number = n - 1; i >= 0; i--) {
13 rightMin[i] = Math.min(rightMin[i + 1], nums[i]);
14 }
15
16 // Initialize answer and left minimum tracker
17 let answer: number = Infinity;
18 let leftMin: number = Infinity;
19
20 // Iterate through array to find valid triplets
21 for (let i: number = 0; i < n; i++) {
22 // Check if current element can be middle of valid triplet
23 // Valid triplet: leftMin < nums[i] > rightMin[i + 1]
24 if (leftMin < nums[i] && rightMin[i + 1] < nums[i]) {
25 // Calculate sum and update minimum answer
26 answer = Math.min(answer, leftMin + nums[i] + rightMin[i + 1]);
27 }
28 // Update left minimum for next iteration
29 leftMin = Math.min(leftMin, nums[i]);
30 }
31
32 // Return result: -1 if no valid triplet found, otherwise the minimum sum
33 return answer === Infinity ? -1 : answer;
34}
35
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array.
- The first loop iterates from
n-1
to0
, performingO(1)
operations in each iteration, contributingO(n)
time. - The second loop iterates through all
n
elements of the array usingenumerate()
, performingO(1)
operations (comparisons and min operations) in each iteration, contributingO(n)
time. - Total time complexity:
O(n) + O(n) = O(n)
The space complexity is O(n)
, where n
is the length of the array.
- The
right
array is initialized with sizen + 1
, requiringO(n)
space. - Other variables (
ans
,left
,i
,x
) use constant spaceO(1)
. - Total space complexity:
O(n) + O(1) = O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Off-by-One Error with Right Minimum Array
The Problem:
A common mistake is incorrectly indexing the min_right
array when checking for valid mountains. Developers often write:
if min_left < current_value and min_right[i] < current_value: # WRONG!
This is incorrect because min_right[i]
includes the current element itself. Since we need the minimum value strictly to the right of position i
, we must use min_right[i + 1]
.
Why This Happens:
- Confusion about what
min_right[i]
represents - Not clearly distinguishing between "from index i" vs "after index i"
Solution:
Always use min_right[i + 1]
when checking for elements to the right of position i
:
if min_left < current_value and min_right[i + 1] < current_value: # CORRECT
Pitfall 2: Incorrect Initialization of Left Minimum
The Problem:
Some developers initialize min_left
with the first element of the array:
min_left = nums[0] # WRONG!
for i in range(1, n):
# check for mountain at position i
This prevents the first element from ever being considered as a potential peak, even if valid mountains exist with index 0 as the peak.
Why This Happens:
- Assumption that we need at least one element on the left before checking
- Misunderstanding that the left minimum should be updated after checking each position
Solution:
Initialize min_left
with infinity and update it after checking each position:
min_left = float('inf')
for i, current_value in enumerate(nums):
# First check if current can be a peak
if min_left < current_value and min_right[i + 1] < current_value:
# Process valid mountain
# Then update min_left for next iterations
min_left = min(min_left, current_value)
Pitfall 3: Using the Actual Minimum Values Instead of Just Tracking Them
The Problem: Attempting to store actual indices or trying to find the exact elements that form the mountain:
left_min_index = -1 right_min_index = -1 # Trying to track which elements form the triplet
This overcomplicates the solution since we only need the minimum possible sum, not the actual triplet indices.
Why This Happens:
- Over-interpreting the problem requirements
- Confusion between finding the minimum sum vs finding the actual triplet
Solution: Focus only on tracking minimum values, not their positions:
# We only need values, not indices
min_left = float('inf') # Just the value
min_right[i] # Just the minimum value to the right
Pitfall 4: Forgetting Edge Cases
The Problem: Not handling arrays that are too small to form a mountain:
# Forgetting to check if n < 3
def minimumSum(self, nums):
n = len(nums)
# Proceeds without checking if mountain is possible
Solution: Add an early check for arrays with fewer than 3 elements:
def minimumSum(self, nums):
n = len(nums)
if n < 3:
return -1 # Cannot form a mountain with less than 3 elements
What's the relationship between a tree and a graph?
Recommended Readings
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!