Facebook Pixel

3745. Maximize Expression of Three Elements

EasyGreedyArrayEnumerationSorting
LeetCode ↗

Problem Description

You are given an integer array nums.

Your task is to choose three elements from the array—call them a, b, and c—located at distinct indices. Using these three chosen elements, you form the expression a + b - c.

The goal is to pick these three elements in a way that maximizes the value of the expression a + b - c.

Return an integer representing the maximum possible value that this expression can reach.

To put it simply:

  • You need exactly three different positions in the array (the indices must be distinct, though the values themselves could repeat).
  • a and b are added together, while c is subtracted.
  • To make a + b - c as large as possible, you want a and b to be the two largest values available, and c to be the smallest value available.

For example, if nums = [1, 3, 6, 2, 4], the two largest values are 6 and 4, and the smallest value is 1. The maximum expression value would be 6 + 4 - 1 = 9.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Greedy Algorithms?

This problem maps to Greedy Algorithms through a short path in the full flowchart.

Computemax/min?yesGreedysolution?yesGreedyAlgorithms

A locally optimal choice at each step builds the globally optimal solution.

Open in Flowchart

Intuition

The expression we want to maximize is a + b - c. Looking at this expression, we can break it into two separate goals that don't interfere with each other:

  • We want a + b to be as large as possible.
  • We want c to be as small as possible (since c is subtracted, a smaller c makes the result bigger).

Since a and b are added together, the way to make their sum as large as possible is to pick the two largest values in the array. Meanwhile, to subtract as little as possible, we should pick the smallest value in the array for c.

A natural worry here is the constraint that the three indices must be distinct. But because the array provides separate positions, the two largest elements and the smallest element will always sit at different indices—even if some of these values happen to be equal, they come from different positions in the array. So as long as the array has at least three elements, we can always assign a, b, and c to three distinct indices without conflict.

This means we don't need to try every combination of three elements. Instead, a single pass through the array is enough: we just keep track of the largest value, the second largest value, and the smallest value as we go. Once we have these three numbers, the answer is simply a + b - c.

Pattern Learn more about Greedy and Sorting patterns.

Solution Approach

Solution 1: Find Maximum, Second Maximum, and Minimum Values

Based on the intuition, we only need three pieces of information from the array: the largest value a, the second largest value b, and the smallest value c. With these, the answer is just a + b - c.

We can gather all three in a single pass through the array, which keeps the algorithm efficient.

We start by initializing three variables:

  • a = -inf and b = -inf to track the two largest values (we start them at negative infinity so any real number will be larger).
  • c = inf to track the smallest value (we start it at positive infinity so any real number will be smaller).

Then, for each element x in nums, we perform two checks:

  1. Update the minimum: If x < c, then x is smaller than the current smallest value, so we update c = x.

  2. Update the top two maximums: This needs a little care to keep a as the largest and b as the second largest:

    • If x >= a, then x is at least as big as the current largest. The old a becomes the new second largest, so we do a, b = x, a. This shifts the previous maximum down into b and places x as the new maximum.
    • Otherwise, if x > b, then x isn't bigger than a but is bigger than the current second largest, so we update b = x.

After scanning the whole array, a and b hold the two largest values and c holds the smallest value. We return a + b - c.

The Python implementation:

class Solution:
    def maximizeExpressionOfThree(self, nums: List[int]) -> int:
        a = b = -inf
        c = inf
        for x in nums:
            if x < c:
                c = x
            if x >= a:
                a, b = x, a
            elif x > b:
                b = x
        return a + b - c

Complexity Analysis:

  • Time Complexity: O(n), where n is the length of nums. We traverse the array only once.
  • Space Complexity: O(1), since we only use a constant number of variables regardless of the input size.

Example Walkthrough

Let's trace through the solution approach using the array nums = [1, 3, 6, 2, 4].

We begin by initializing our three tracking variables:

  • a = -inf (largest value)
  • b = -inf (second largest value)
  • c = inf (smallest value)

Now we walk through each element, performing the two checks (update minimum, then update top two maximums).

Step 1: x = 1

  • Minimum check: 1 < inf → true, so c = 1.
  • Maximum check: 1 >= -inf → true, so a, b = 1, -inf. Now a = 1, b = -inf.
  • State: a = 1, b = -inf, c = 1.

Step 2: x = 3

  • Minimum check: 3 < 1 → false, c stays 1.
  • Maximum check: 3 >= 1 → true, so a, b = 3, 1. The old a (which was 1) slides into b.
  • State: a = 3, b = 1, c = 1.

Step 3: x = 6

  • Minimum check: 6 < 1 → false, c stays 1.
  • Maximum check: 6 >= 3 → true, so a, b = 6, 3. The old maximum 3 slides into b.
  • State: a = 6, b = 3, c = 1.

Step 4: x = 2

  • Minimum check: 2 < 1 → false, c stays 1.
  • Maximum check: 2 >= 6 → false. Then check 2 > 3 → false, so b stays 3.
  • State: a = 6, b = 3, c = 1.

Step 5: x = 4

  • Minimum check: 4 < 1 → false, c stays 1.
  • Maximum check: 4 >= 6 → false. Then check 4 > 3 → true, so b = 4.
  • State: a = 6, b = 4, c = 1.

Final result:

After the single pass, we have a = 6 (largest), b = 4 (second largest), and c = 1 (smallest). The answer is:

a + b - c = 6 + 4 - 1 = 9

This matches our expectation: the two largest values 6 and 4 are added, and the smallest value 1 is subtracted, all coming from three distinct indices.

Solution Implementation

1from math import inf
2from typing import List
3
4
5class Solution:
6    def maximizeExpressionOfThree(self, nums: List[int]) -> int:
7        # Track the two largest values (largest, second_largest)
8        # and the single smallest value (smallest).
9        largest = second_largest = -inf
10        smallest = inf
11
12        for num in nums:
13            # Update the smallest value seen so far.
14            if num < smallest:
15                smallest = num
16
17            # Update the two largest values.
18            if num >= largest:
19                # New maximum found: shift the old largest down to second place.
20                largest, second_largest = num, largest
21            elif num > second_largest:
22                # Not a new max, but larger than the current runner-up.
23                second_largest = num
24
25        # Maximize the expression: largest + second_largest - smallest.
26        return largest + second_largest - smallest
27
1class Solution {
2    public int maximizeExpressionOfThree(int[] nums) {
3        // Use a large sentinel value as the boundary for comparisons.
4        final int inf = 1 << 30;
5
6        // largest  -> the maximum value seen so far
7        // secondLargest -> the second maximum value seen so far
8        // smallest -> the minimum value seen so far
9        int largest = -inf;
10        int secondLargest = -inf;
11        int smallest = inf;
12
13        for (int x : nums) {
14            // Track the smallest value (used to subtract, maximizing the result).
15            if (x < smallest) {
16                smallest = x;
17            }
18
19            // Maintain the top two largest values.
20            if (x >= largest) {
21                // The current largest gets demoted to second largest,
22                // and x becomes the new largest.
23                secondLargest = largest;
24                largest = x;
25            } else if (x > secondLargest) {
26                // x is not the largest, but it is larger than the second largest.
27                secondLargest = x;
28            }
29        }
30
31        // Maximize the expression: (top two largest) minus the smallest value.
32        return largest + secondLargest - smallest;
33    }
34}
35
1class Solution {
2public:
3    int maximizeExpressionOfThree(vector<int>& nums) {
4        const int kInf = 1 << 30;
5
6        // largest and secondLargest track the two greatest values (for a + b)
7        // smallest tracks the minimum value (for -c)
8        int largest = -kInf;
9        int secondLargest = -kInf;
10        int smallest = kInf;
11
12        for (int x : nums) {
13            // Update the smallest value seen so far
14            if (x < smallest) {
15                smallest = x;
16            }
17
18            // Update the two largest values seen so far
19            if (x >= largest) {
20                // x becomes the new largest; old largest demotes to secondLargest
21                secondLargest = largest;
22                largest = x;
23            } else if (x > secondLargest) {
24                // x is not the largest but exceeds the current secondLargest
25                secondLargest = x;
26            }
27        }
28
29        // Maximize the expression: sum of two greatest minus the smallest
30        return largest + secondLargest - smallest;
31    }
32};
33
1/**
2 * Maximizes the expression (max1 + max2 - min) by selecting:
3 * - the two largest values in the array (to add)
4 * - the smallest value in the array (to subtract)
5 *
6 * @param nums - the input array of numbers (expected to have at least 3 elements)
7 * @returns the maximum value of (largest + secondLargest - smallest)
8 */
9function maximizeExpressionOfThree(nums: number[]): number {
10    // A sufficiently large sentinel value used to initialize extremes.
11    const infinity = 1 << 30;
12
13    // largest: the maximum value seen so far.
14    // secondLargest: the second maximum value seen so far.
15    // smallest: the minimum value seen so far.
16    let largest = -infinity;
17    let secondLargest = -infinity;
18    let smallest = infinity;
19
20    // Single pass to find the two largest values and the smallest value.
21    for (const num of nums) {
22        // Update the smallest value if the current number is smaller.
23        if (num < smallest) {
24            smallest = num;
25        }
26
27        // Update the two largest values.
28        if (num >= largest) {
29            // Current number becomes the new largest;
30            // the previous largest is demoted to second largest.
31            secondLargest = largest;
32            largest = num;
33        } else if (num > secondLargest) {
34            // Current number is between secondLargest and largest.
35            secondLargest = num;
36        }
37    }
38
39    // The maximized expression: sum of the two largest minus the smallest.
40    return largest + secondLargest - smallest;
41}
42

Time and Space Complexity

  • Time complexity: 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 constant-time comparisons and assignments for each element x. Thus the overall time cost grows linearly with the size of the input.

  • Space complexity: O(1). Only a fixed number of extra variables (a, b, and c) are used to track the two largest values and the smallest value, regardless of the input size. No additional data structures that scale with n are allocated.

Pattern Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Allowing the Same Index to Be Used for Multiple Roles

The most common mistake is failing to respect the distinct indices constraint. Because a and b are the two largest values while c is the smallest, beginners sometimes worry whether one element could be "double-counted"—for instance, if the smallest value is also one of the two largest (which can only happen when the array is very small or has repeated values).

Why it's usually fine: When nums has at least 3 elements, picking the top two values for a, b and the bottom one for c always lands on three distinct indices. The largest and second-largest are tracked at separate positions (we only shift a into b, never reuse the same slot), and the smallest is a third position. Even if values repeat (e.g., [5, 5, 5]), the indices themselves are still distinct, which is all the problem requires.

The real danger appears if you mistakenly track second_largest by reusing the index of largest. Consider the naive update below:

# WRONG: second_largest can end up equal to largest's value via the same element
if num >= largest:
    largest = num
if num > second_largest:   # missing 'elif' — same num updates both!
    second_largest = num

Here, a single element num can update both largest and second_largest in the same iteration, effectively using one index twice.

Solution: Use elif to guarantee that each element updates at most one of the two maximum slots in a single pass:

if num >= largest:
    largest, second_largest = num, largest
elif num > second_largest:
    second_largest = num

This shifts the previous largest into second_largest whenever a new maximum arrives, so the two always come from different elements.

Pitfall 2: Forgetting to Update the Minimum Independently

Another subtle error is bundling the minimum update inside the elif chain of the maximum logic. The smallest value must be checked with its own independent if, not tied to the max comparisons:

# WRONG: minimum nested inside the max branch is never reached for large values
if num >= largest:
    largest, second_largest = num, largest
elif num > second_largest:
    second_largest = num
elif num < smallest:        # large numbers skip this entirely!
    smallest = num

A large value satisfies the first if, so the smallest check never runs for it—but that's not the issue. The real bug is that this structure prevents proper tracking when a value is simultaneously a candidate for both extremes in edge cases.

Solution: Keep the minimum update as a separate, unconditional if so every element is considered for the smallest slot:

if num < smallest:
    smallest = num

if num >= largest:
    largest, second_largest = num, largest
elif num > second_largest:
    second_largest = num

Pitfall 3: Integer Overflow in Other Languages

In Python, integers are arbitrary-precision, so largest + second_largest - smallest never overflows. However, when porting this solution to Java or C++, the sum of two large int values (e.g., both near 2^31 - 1) can overflow a 32-bit integer.

Solution: Use a wider type (long in Java/C++) for the accumulated result, or initialize the bounds with Long.MIN_VALUE / LONG_MAX rather than int limits:

long result = (long) largest + second_largest - smallest;

This safeguards the final computation against overflow when the array contains values near the integer boundaries.

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

How many times is a tree node visited in a depth first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More