{x}
blog image

Maximum Product of Two Elements in an Array

Given the array of integers nums, you will choose two different indices i and j of that array. Return the maximum value of (nums[i]-1)*(nums[j]-1).

 

Example 1:

Input: nums = [3,4,5,2]
Output: 12 
Explanation: If you choose the indices i=1 and j=2 (indexed from 0), you will get the maximum value, that is, (nums[1]-1)*(nums[2]-1) = (4-1)*(5-1) = 3*4 = 12. 

Example 2:

Input: nums = [1,5,4,5]
Output: 16
Explanation: Choosing the indices i=1 and j=3 (indexed from 0), you will get the maximum value of (5-1)*(5-1) = 16.

Example 3:

Input: nums = [3,7]
Output: 12

 

Constraints:

  • 2 <= nums.length <= 500
  • 1 <= nums[i] <= 10^3

Solution Explanation for Maximum Product of Two Elements in an Array

The problem asks to find the maximum product of two distinct elements in an array after subtracting 1 from each element. We can solve this efficiently using several approaches.

Understanding the Problem

The key insight is that to maximize the product (nums[i] - 1) * (nums[j] - 1), we need to select the two largest numbers from the input array nums. Subtracting 1 from each before multiplying ensures that we are considering the largest possible difference in values.

Approach 1: Brute Force

This approach iterates through all possible pairs of distinct elements in the array, calculates the product, and keeps track of the maximum product encountered.

  • Time Complexity: O(n^2), as we have nested loops iterating through all pairs of elements.
  • Space Complexity: O(1), as we use only a constant amount of extra space.

Approach 2: Sorting

This approach sorts the array in descending order and then takes the product of the two largest elements (after subtracting 1 from each). Sorting allows us to efficiently find the two largest elements without comparing all pairs.

  • Time Complexity: O(n log n), dominated by the sorting algorithm.
  • Space Complexity: O(1) or O(n) depending on the sorting algorithm used. In-place sorting algorithms like heapsort have O(1) space complexity, while merge sort may have O(n) space complexity.

Approach 3: Linear Scan

This approach iterates through the array only once, keeping track of the two largest elements encountered so far. This is the most efficient approach as it avoids sorting.

  • Time Complexity: O(n), as we iterate through the array once.
  • Space Complexity: O(1), as we use only a constant number of variables to store the two largest elements.

Code Examples (Python)

Here are implementations of approaches 2 and 3 in Python:

Approach 2 (Sorting):

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        nums.sort(reverse=True)  # Sort in descending order
        return (nums[0] - 1) * (nums[1] - 1)

Approach 3 (Linear Scan):

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        max1 = 0
        max2 = 0
        for num in nums:
            if num > max1:
                max2 = max1
                max1 = num
            elif num > max2:
                max2 = num
        return (max1 - 1) * (max2 - 1)
 

Other Languages:

The same approaches can be implemented in other languages (Java, C++, JavaScript, etc.). The core logic remains the same; the primary difference lies in the syntax and available libraries for sorting. For instance, in Java, you would use Arrays.sort() for Approach 2, and in C++, you'd use std::sort().

Choosing the Best Approach

For this problem, Approach 3 (Linear Scan) is the most efficient due to its O(n) time complexity and O(1) space complexity. It's generally preferred unless the array is already sorted (or the sorting is done for other reasons in the code). Approach 2 is acceptable if you already have a need to sort the array for other operations. Approach 1 should be avoided unless the array is very small.