2874. Maximum Value of an Ordered Triplet II
Problem Description
You are given a 0-indexed integer array nums
.
Your task is to find the maximum value among all possible triplets of indices (i, j, k)
where i < j < k
. The value of a triplet is calculated using the formula: (nums[i] - nums[j]) * nums[k]
.
The goal is to return the maximum value that can be obtained from any valid triplet. If all possible triplets produce negative values, return 0
.
For example, if you have indices i
, j
, and k
with i < j < k
, you would:
- Take the element at index
i
:nums[i]
- Subtract the element at index
j
:nums[i] - nums[j]
- Multiply the result by the element at index
k
:(nums[i] - nums[j]) * nums[k]
You need to find which combination of three indices gives you the largest possible value according to this formula.
Intuition
To find the maximum value of (nums[i] - nums[j]) * nums[k]
where i < j < k
, let's think about what we need at each position.
When we're at position k
, we want to maximize (nums[i] - nums[j]) * nums[k]
for all valid i
and j
before k
. If nums[k]
is positive, we want the maximum possible value of (nums[i] - nums[j])
. If nums[k]
is negative, we'd want the minimum value of (nums[i] - nums[j])
, but since we're looking for the maximum triplet value and can return 0
if all values are negative, we can focus on maximizing (nums[i] - nums[j])
.
Now, how do we maximize (nums[i] - nums[j])
for positions before k
? We need the largest nums[i]
and the smallest nums[j]
where i < j
. But there's a constraint: i
must come before j
.
The key insight is that as we iterate through the array, we can maintain:
- The maximum value seen so far (this could be a potential
nums[i]
) - The maximum difference
(nums[i] - nums[j])
seen so far using valid pairs
When we reach a new element, it can play three roles:
- It can be
nums[k]
for all previous valid(i, j)
pairs - It can be
nums[j]
paired with all previous maximum values asnums[i]
- It can be a future
nums[i]
for upcoming elements
This suggests a single-pass solution where we maintain the maximum element seen and the maximum difference seen, updating our answer as we process each element as a potential nums[k]
.
Solution Approach
We implement a single-pass solution using three variables to track the necessary information as we iterate through the array:
ans
: Maintains the maximum triplet value found so farmx
: Tracks the maximum element seen up to the current position (potentialnums[i]
)mx_diff
: Stores the maximum difference(nums[i] - nums[j])
for all valid pairs before the current position
The algorithm processes each element x
in the array as a potential nums[k]
:
Step-by-step process for each element x
:
-
Calculate triplet value: First, we compute
mx_diff * x
. This gives us the maximum possible triplet value whenx
serves asnums[k]
, using the best(nums[i] - nums[j])
pair found before this position. We updateans = max(ans, mx_diff * x)
. -
Update maximum difference: Next, we calculate
mx - x
. This represents a new potential difference wheremx
(the largest element seen so far) acts asnums[i]
and the current elementx
acts asnums[j]
. We updatemx_diff = max(mx_diff, mx - x)
to maintain the best difference for future elements. -
Update maximum element: Finally, we update
mx = max(mx, x)
to ensure we always have the largest element seen so far, which could serve asnums[i]
for future pairs.
The order of these updates is crucial:
- We must calculate the answer before updating
mx_diff
becausex
is acting asnums[k]
and needs the differences from strictly earlier pairs - We must update
mx_diff
before updatingmx
because we need the old maximum to calculate the new difference withx
asnums[j]
After processing all elements, we return ans
, which automatically handles the case where all triplets are negative (since ans
starts at 0).
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 the array nums = [3, 1, 5, 2, 4]
.
We initialize:
ans = 0
(maximum triplet value found)mx = 3
(first element)mx_diff = 0
(no valid difference yet)
Processing index 1 (x = 1):
- Calculate triplet value:
mx_diff * x = 0 * 1 = 0
, soans = max(0, 0) = 0
- Update max difference:
mx - x = 3 - 1 = 2
, somx_diff = max(0, 2) = 2
- This means we now have a valid pair where
nums[0]=3
is i andnums[1]=1
is j
- This means we now have a valid pair where
- Update max element:
mx = max(3, 1) = 3
Processing index 2 (x = 5):
- Calculate triplet value:
mx_diff * x = 2 * 5 = 10
, soans = max(0, 10) = 10
- This represents the triplet (0,1,2) with value
(3-1)*5 = 10
- This represents the triplet (0,1,2) with value
- Update max difference:
mx - x = 3 - 5 = -2
, somx_diff = max(2, -2) = 2
- Update max element:
mx = max(3, 5) = 5
Processing index 3 (x = 2):
- Calculate triplet value:
mx_diff * x = 2 * 2 = 4
, soans = max(10, 4) = 10
- Update max difference:
mx - x = 5 - 2 = 3
, somx_diff = max(2, 3) = 3
- Now our best difference is from pair (2,3) where
nums[2]=5
is i andnums[3]=2
is j
- Now our best difference is from pair (2,3) where
- Update max element:
mx = max(5, 2) = 5
Processing index 4 (x = 4):
- Calculate triplet value:
mx_diff * x = 3 * 4 = 12
, soans = max(10, 12) = 12
- This represents the triplet (2,3,4) with value
(5-2)*4 = 12
- This represents the triplet (2,3,4) with value
- Update max difference:
mx - x = 5 - 4 = 1
, somx_diff = max(3, 1) = 3
- Update max element:
mx = max(5, 4) = 5
The maximum triplet value is 12, achieved by indices (2,3,4) with calculation (nums[2] - nums[3]) * nums[4] = (5 - 2) * 4 = 12
.
Solution Implementation
1class Solution:
2 def maximumTripletValue(self, nums: List[int]) -> int:
3 # Initialize variables to track the maximum triplet value and intermediate values
4 max_triplet_value = 0 # Stores the maximum value of (nums[i] - nums[j]) * nums[k]
5 max_element = 0 # Tracks the maximum element seen so far (potential nums[i])
6 max_difference = 0 # Tracks the maximum value of (nums[i] - nums[j]) seen so far
7
8 # Iterate through each element in the array
9 for current_num in nums:
10 # Update the maximum triplet value
11 # current_num acts as nums[k], and max_difference is the best (nums[i] - nums[j]) so far
12 max_triplet_value = max(max_triplet_value, max_difference * current_num)
13
14 # Update the maximum difference
15 # current_num acts as nums[j], and max_element is the best nums[i] seen so far
16 max_difference = max(max_difference, max_element - current_num)
17
18 # Update the maximum element seen so far
19 # current_num could be a potential nums[i] for future iterations
20 max_element = max(max_element, current_num)
21
22 return max_triplet_value
23
1class Solution {
2 public long maximumTripletValue(int[] nums) {
3 // Initialize the maximum triplet value result
4 long maxTripletValue = 0;
5
6 // Track the maximum difference (nums[i] - nums[j]) seen so far where i < j
7 long maxDifference = 0;
8
9 // Track the maximum element seen so far
10 int maxElement = 0;
11
12 // Iterate through each element in the array
13 for (int currentNum : nums) {
14 // Update max triplet value: (nums[i] - nums[j]) * nums[k] where i < j < k
15 // currentNum acts as nums[k], and maxDifference represents the best (nums[i] - nums[j])
16 maxTripletValue = Math.max(maxTripletValue, maxDifference * currentNum);
17
18 // Update max difference for future iterations
19 // currentNum acts as nums[j], and maxElement represents the best nums[i] seen before
20 maxDifference = Math.max(maxDifference, maxElement - currentNum);
21
22 // Update the maximum element seen so far
23 // currentNum could be nums[i] for future triplets
24 maxElement = Math.max(maxElement, currentNum);
25 }
26
27 return maxTripletValue;
28 }
29}
30
1class Solution {
2public:
3 long long maximumTripletValue(vector<int>& nums) {
4 // Initialize variables to track the maximum triplet value
5 long long maxTripletValue = 0; // Stores the maximum value of (nums[i] - nums[j]) * nums[k]
6 long long maxDifference = 0; // Stores the maximum value of (nums[i] - nums[j]) seen so far
7 int maxElement = 0; // Stores the maximum element seen so far
8
9 // Iterate through each element in the array
10 for (int currentNum : nums) {
11 // Update the maximum triplet value
12 // This represents (nums[i] - nums[j]) * nums[k] where k is the current index
13 maxTripletValue = max(maxTripletValue, maxDifference * currentNum);
14
15 // Update the maximum difference (nums[i] - nums[j])
16 // where i < j and j is the current index
17 maxDifference = max(maxDifference, static_cast<long long>(maxElement - currentNum));
18
19 // Update the maximum element seen so far
20 // This will be used as nums[i] for future iterations
21 maxElement = max(maxElement, currentNum);
22 }
23
24 return maxTripletValue;
25 }
26};
27
1/**
2 * Finds the maximum value of (nums[i] - nums[j]) * nums[k] where i < j < k
3 * @param nums - Array of numbers to process
4 * @returns Maximum triplet value
5 */
6function maximumTripletValue(nums: number[]): number {
7 // Initialize tracking variables
8 let maxTripletValue: number = 0; // Stores the maximum triplet value found so far
9 let maxElement: number = 0; // Tracks the maximum element seen so far
10 let maxDifference: number = 0; // Tracks the maximum difference (nums[i] - nums[j]) seen so far
11
12 // Iterate through each element in the array
13 for (const currentElement of nums) {
14 // Update the maximum triplet value
15 // This represents (nums[i] - nums[j]) * nums[k] where current element is nums[k]
16 maxTripletValue = Math.max(maxTripletValue, maxDifference * currentElement);
17
18 // Update the maximum difference for future iterations
19 // This represents the best possible (nums[i] - nums[j]) seen so far
20 maxDifference = Math.max(maxDifference, maxElement - currentElement);
21
22 // Update the maximum element seen so far
23 // This will be used as nums[i] in future difference calculations
24 maxElement = Math.max(maxElement, currentElement);
25 }
26
27 return maxTripletValue;
28}
29
Time and Space Complexity
Time Complexity: O(n)
The algorithm performs a single pass through the input array nums
of length n
. In each iteration, it performs only constant-time operations:
- Three
max()
comparisons with fixed number of arguments - One multiplication operation
- Variable updates
Since we iterate through each element exactly once and perform O(1)
operations per element, the overall time complexity is O(n)
.
Space Complexity: O(1)
The algorithm uses only a fixed amount of extra space regardless of input size:
- Three integer variables:
ans
,mx
, andmx_diff
- One loop variable:
x
These variables do not scale with the input size n
, so the space complexity is constant, O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Incorrect Update Order
The Problem: A common mistake is updating the variables in the wrong order within the loop. For example:
# WRONG ORDER - This will produce incorrect results
for current_num in nums:
max_element = max(max_element, current_num) # Updated too early!
max_difference = max(max_difference, max_element - current_num)
max_triplet_value = max(max_triplet_value, max_difference * current_num)
When you update max_element
first, you're including the current element in the calculation of max_difference
, which violates the constraint that i < j
. The current element cannot simultaneously be both nums[i]
and nums[j]
.
The Solution: Always maintain the correct update sequence:
- First calculate the triplet value (current element as
nums[k]
) - Then update the maximum difference (current element as
nums[j]
) - Finally update the maximum element (current element as potential future
nums[i]
)
Pitfall 2: Using Insufficient Initial Values
The Problem: Initializing tracking variables with values that are too small can cause issues with negative numbers:
# WRONG - May miss valid negative differences max_element = 0 max_difference = 0
If the array starts with negative numbers like [-5, -3, 2, 8]
, initializing max_element
to 0 means we'd miss the valid difference of (-5) - (-3) = -2
in early iterations.
The Solution: Two approaches work:
- Initialize with the first element:
max_element = nums[0]
(requires checking array length) - Use negative infinity:
max_element = float('-inf')
to ensure all actual values are considered
Pitfall 3: Misunderstanding Variable Roles
The Problem: Confusing what each variable represents at different points in the iteration:
# Conceptual error - thinking max_difference can use current element
for current_num in nums:
# WRONG thinking: "I can use current_num in max_difference calculation"
max_triplet_value = max(max_triplet_value, (current_num - something) * something_else)
The Solution: Remember the temporal relationship:
- When processing element at index
k
,max_difference
contains the best(nums[i] - nums[j])
where bothi
andj
are strictly less thank
- The current element can only play one role per iteration: it's
nums[k]
for the answer calculation, then becomes a potentialnums[j]
for difference calculation, and finally a potentialnums[i]
for future iterations
Pitfall 4: Off-by-One Errors with Index Constraints
The Problem: Attempting to manually track indices and making errors with the strict inequality requirements:
# WRONG - Trying to manually ensure i < j < k with complex logic if current_index > 1: # Ensuring at least 3 elements if previous_index < current_index - 1: # Complex and error-prone # calculate...
The Solution: The beauty of this algorithm is that it implicitly maintains the ordering constraint through the update sequence. By updating variables in the correct order, you automatically ensure that:
max_element
comes from indices before the currentj
max_difference
comes from pairs before the currentk
- No explicit index tracking is needed
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
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!