2873. Maximum Value of an Ordered Triplet I
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 (i, j, k)
is calculated as (nums[i] - nums[j]) * nums[k]
.
If all possible triplets result in a negative value, you should return 0
.
The key insight is that for each position k
, you want to maximize (nums[i] - nums[j]) * nums[k]
where i < j < k
. This can be done efficiently by tracking:
- The maximum value seen so far (which could be
nums[i]
) - The maximum difference
nums[i] - nums[j]
seen so far for validi < j
As you iterate through the array, each element can play the role of nums[k]
, and you can use the previously computed maximum difference to calculate potential triplet values. The solution maintains these values in a single pass through the array, updating the maximum triplet value, the maximum difference, and the maximum element as it progresses.
Intuition
To maximize (nums[i] - nums[j]) * nums[k]
where i < j < k
, let's think about what we need when we're at position k
.
At any position k
, we want to multiply nums[k]
with the largest possible value of (nums[i] - nums[j])
where both i
and j
come before k
. This means we need to track the maximum difference we've seen so far.
But how do we maintain the maximum difference (nums[i] - nums[j])
efficiently? For this difference to be large, we want nums[i]
to be as large as possible and nums[j]
to be as small as possible, with i < j
.
Here's the key realization: as we iterate through the array, each element can play different roles:
- When we see element at position
k
, it can be used as the third elementnums[k]
in our triplet - The same element can also be used as
nums[j]
(the middle element) for future triplets - It can also potentially be
nums[i]
(the first element) for even later triplets
This suggests we can solve the problem in a single pass by maintaining:
- The maximum value seen so far (this could serve as
nums[i]
) - The maximum difference
nums[i] - nums[j]
seen so far (wherei < j
) - The maximum triplet value found so far
As we move through the array, for each element x
:
- First, we try using
x
asnums[k]
and multiply it with the best(nums[i] - nums[j])
we've found - Then, we update our maximum difference by considering
x
as a potentialnums[j]
with the bestnums[i]
we've seen - Finally, we update our maximum value by considering
x
as a potential futurenums[i]
This ordering is crucial - we must update in this sequence to ensure we're only using valid indices where i < j < k
.
Solution Approach
The solution uses a single-pass algorithm with three variables to track the necessary values:
ans
: Maintains the maximum triplet value found so farmx_diff
: Tracks the maximum difference(nums[i] - nums[j])
wherei < j
mx
: Stores the maximum value seen so far in the array
The algorithm iterates through each element x
in the array, treating it as nums[k]
:
Step 1: Update the answer
ans = max(ans, mx_diff * x)
At this point, mx_diff
contains the best (nums[i] - nums[j])
from all valid pairs before the current position. We multiply it by the current element x
(acting as nums[k]
) to get a potential triplet value and update our answer if this value is larger.
Step 2: Update the maximum difference
mx_diff = max(mx_diff, mx - x)
Now we consider x
as a potential nums[j]
for future triplets. We calculate mx - x
where mx
is the largest element seen before x
(potential nums[i]
). This gives us a new difference that might be used with future elements as nums[k]
.
Step 3: Update the maximum value
mx = max(mx, x)
Finally, we update mx
to include the current element x
, as it could serve as nums[i]
for future triplets.
Why this order matters:
- We must calculate the triplet value before updating
mx_diff
to ensure we're using differences from strictly earlier positions - We must update
mx_diff
before updatingmx
to ensure the difference is calculated with elements wherei < j
- This ordering naturally maintains the constraint
i < j < k
The algorithm returns ans
after processing all elements. If all triplets are negative, ans
remains 0
(its initial value), which is the correct output for that case.
Time Complexity: O(n)
- single pass through the array
Space Complexity: O(1)
- only using a constant amount of extra space
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 = [10, 13, 5, 18]
.
We want to find the maximum value of (nums[i] - nums[j]) * nums[k]
where i < j < k
.
Initial state:
ans = 0
(maximum triplet value)mx_diff = 0
(maximum difference)mx = 0
(maximum value seen)
Iteration 1: x = 10 (index 0)
- Calculate triplet:
ans = max(0, 0 * 10) = 0
- No valid triplet yet (need at least 3 elements)
- Update difference:
mx_diff = max(0, 0 - 10) = 0
- Can't form a valid difference yet
- Update max:
mx = max(0, 10) = 10
- 10 could be nums[i] for future triplets
Iteration 2: x = 13 (index 1)
- Calculate triplet:
ans = max(0, 0 * 13) = 0
- Still no valid triplet (need i < j < k)
- Update difference:
mx_diff = max(0, 10 - 13) = 0
- Difference is negative, keep 0
- Update max:
mx = max(10, 13) = 13
- 13 is now the largest potential nums[i]
Iteration 3: x = 5 (index 2)
- Calculate triplet:
ans = max(0, 0 * 5) = 0
- Using x=5 as nums[k], but mx_diff is still 0
- Update difference:
mx_diff = max(0, 13 - 5) = 8
- Now we have a valid difference! (13 at index 1) - (5 at index 2) = 8
- Update max:
mx = max(13, 5) = 13
- 13 remains the largest
Iteration 4: x = 18 (index 3)
- Calculate triplet:
ans = max(0, 8 * 18) = 144
- Using x=18 as nums[k] and mx_diff=8 from (13-5)
- This represents the triplet (1, 2, 3): (13 - 5) * 18 = 144
- Update difference:
mx_diff = max(8, 13 - 18) = 8
- Keep the better difference of 8
- Update max:
mx = max(13, 18) = 18
- 18 is now the largest
Result: Return ans = 144
The maximum triplet value is 144, coming from indices (1, 2, 3) where:
- i = 1: nums[1] = 13
- j = 2: nums[2] = 5
- k = 3: nums[3] = 18
- Value: (13 - 5) * 18 = 8 * 18 = 144
Solution Implementation
1class Solution:
2 def maximumTripletValue(self, nums: List[int]) -> int:
3 # Initialize variables to track the maximum triplet value
4 max_triplet_value = 0 # Maximum value of (nums[i] - nums[j]) * nums[k]
5 max_value_seen = 0 # Maximum value seen so far (potential nums[i])
6 max_difference = 0 # Maximum value of (nums[i] - nums[j]) seen so far
7
8 # Iterate through each number in the array
9 for current_num in nums:
10 # Update max triplet value: max_difference represents best (nums[i] - nums[j])
11 # and current_num represents nums[k]
12 max_triplet_value = max(max_triplet_value, max_difference * current_num)
13
14 # Update max difference: max_value_seen represents best nums[i] so far
15 # and current_num represents nums[j]
16 max_difference = max(max_difference, max_value_seen - current_num)
17
18 # Update the maximum value seen so far
19 max_value_seen = max(max_value_seen, current_num)
20
21 return max_triplet_value
22
1class Solution {
2 public long maximumTripletValue(int[] nums) {
3 // Initialize the maximum triplet value found so far
4 long maxTripletValue = 0;
5
6 // Track the maximum value of (nums[i] - nums[j]) where i < j < current index
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]
15 // where maxDifference represents the best (nums[i] - nums[j]) found so far
16 // and currentNum represents nums[k]
17 maxTripletValue = Math.max(maxTripletValue, maxDifference * currentNum);
18
19 // Update max difference: nums[i] - nums[j]
20 // where maxElement represents the best nums[i] found so far
21 // and currentNum represents nums[j]
22 maxDifference = Math.max(maxDifference, maxElement - currentNum);
23
24 // Update the maximum element seen so far
25 maxElement = Math.max(maxElement, currentNum);
26 }
27
28 return maxTripletValue;
29 }
30}
31
1class Solution {
2public:
3 long long maximumTripletValue(vector<int>& nums) {
4 // Initialize result and tracking variables
5 long long result = 0; // Maximum triplet value found so far
6 long long maxDifference = 0; // Maximum value of (nums[i] - nums[j]) where i < j
7 int maxValue = 0; // Maximum value seen so far
8
9 // Iterate through the array once
10 for (int currentNum : nums) {
11 // Update result: maxDifference represents max(nums[i] - nums[j]) for all i < j before current position
12 // Multiplying by current number gives us (nums[i] - nums[j]) * nums[k] where i < j < k
13 result = max(result, maxDifference * currentNum);
14
15 // Update maxDifference: the maximum difference between any previous maximum and current number
16 // This will be used for future k values
17 maxDifference = max(maxDifference, static_cast<long long>(maxValue - currentNum));
18
19 // Update the maximum value seen so far
20 maxValue = max(maxValue, currentNum);
21 }
22
23 return result;
24 }
25};
26
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 according to the formula
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 maxElementSeen: number = 0; // Tracks the maximum element seen up to current position
10 let maxDifference: number = 0; // Tracks the maximum value of (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 (nums[i] - nums[j])
19 // where maxElementSeen could be nums[i] and currentElement could be nums[j]
20 maxDifference = Math.max(maxDifference, maxElementSeen - currentElement);
21
22 // Update the maximum element seen so far
23 maxElementSeen = Math.max(maxElementSeen, currentElement);
24 }
25
26 return maxTripletValue;
27}
28
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm iterates through the array exactly once with a single for loop, performing a constant number of operations (comparisons and assignments) for each element.
The space complexity is O(1)
. The algorithm only uses three auxiliary variables (ans
, mx
, and mx_diff
) to track the maximum values throughout the iteration, regardless of the input size. No additional data structures that scale with input size are created.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Incorrect Variable Update Order
One of the most common mistakes is updating the tracking variables in the wrong order. Consider this incorrect implementation:
# WRONG ORDER!
for current_num in nums:
max_value_seen = max(max_value_seen, current_num) # Updated too early!
max_difference = max(max_difference, max_value_seen - current_num)
max_triplet_value = max(max_triplet_value, max_difference * current_num)
Why this fails: By updating max_value_seen
before calculating the difference, we're allowing current_num
to be both nums[i]
and nums[j]
in the same iteration. This violates the constraint that i < j < k
and can produce incorrect results.
Example where it fails:
- Input:
[5, 6, 7]
- At index 1 (value 6):
- Wrong order would update
max_value_seen
to 6 first - Then calculate difference as
6 - 6 = 0
- Missing the valid difference
5 - 6 = -1
- Wrong order would update
Pitfall 2: Not Handling Zero or Negative Results
Another mistake is assuming the answer must be positive and initializing variables incorrectly:
# WRONG INITIALIZATION!
max_triplet_value = float('-inf') # Should be 0
max_difference = float('-inf') # Should be 0
Why this fails: The problem states that if all triplets are negative, we should return 0. By initializing to negative infinity, we might return a negative value instead of 0.
Pitfall 3: Misunderstanding the Roles of Variables
Developers might confuse what each variable represents at different points in the iteration:
# CONCEPTUAL ERROR!
for k, current_num in enumerate(nums):
# Trying to use current_num as nums[i] when it should be nums[k]
max_difference = max(max_difference, current_num - max_value_seen) # WRONG!
Solution to Avoid These Pitfalls:
- Maintain strict update order: Always update in the sequence: answer → difference → maximum value
- Initialize to zero: Start with
max_triplet_value = 0
,max_difference = 0
, andmax_value_seen = 0
- Clear variable naming: Use descriptive names that indicate the role of each variable
- Add comments: Document what each variable represents and why the update order matters
Correct implementation pattern:
def maximumTripletValue(self, nums: List[int]) -> int:
ans = 0 # Maximum triplet value (return 0 if all negative)
mx_diff = 0 # Best (nums[i] - nums[j]) where i < j
mx = 0 # Maximum value seen so far
for x in nums:
# x is nums[k] for this calculation
ans = max(ans, mx_diff * x)
# x becomes nums[j] for future iterations
mx_diff = max(mx_diff, mx - x)
# x becomes potential nums[i] for future iterations
mx = max(mx, x)
return ans
Consider the classic dynamic programming of longest increasing subsequence:
Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
For example, the length of LIS for [50, 3, 10, 7, 40, 80]
is 4
and LIS is
[3, 7, 40, 80]
.
What is the recurrence relation?
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!