Problem

Given an integer array nums, find a subarray that has the largest product, and return the product.

The test cases are generated so that the answer will fit in a 32-bit integer.

OR

Given an integer array with negative numbers and zero find the subarray with maximum product, i.e. find the contiguous array elements that produce maximum product.

Examples

Example 1:

Input: nums = [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.

Example 2:

Input: nums = [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.

Solution

Whenever a 0 appears, it splits the array into separate subarrays. We need to find the maximum product for each of these subarrays and return the largest product. Within each subarray, if there is an even number of negative elements, the maximum product is the product of the entire subarray. If there is an odd number of negative elements, form two subarrays—one excluding the leftmost negative element and the other excluding the rightmost negative element. The larger product of these two will be the result.

Video explanation

Here is the video explaining this method in detail. Please check it out:

Method 1 - Naive

Here is the naive approach:

  1. Generate All Subarrays:
    • Use two nested loops.
    • The first loop (i) starts the subarray.
    • The second loop (j) ends the subarray.
  2. Calculate Product:
    • For each subarray, calculate the product of its elements.
    • Keep track of the maximum product encountered.
  3. Return Result:
    • Once all subarrays have been processed, the maximum product is returned.

Code

Java
class Solution {
    public int maxProductNaive(int[] nums) {
        int ans = Integer.MIN_VALUE; // Start with smallest possible value
        int n = nums.length;
        
        for (int i = 0; i < n; i++) {
            int prod = 1;
            for (int j = i; j < n; j++) {
                prod *= nums[j];
                ans = Math.max(ans, prod);
            }
        }
        
        return ans;
    }
}
Python
class Solution:
    def maxProductNaive(self, nums: List[int]) -> int:
        ans: int = float('-inf')  # Start with smallest possible value
        n: int = len(nums)
        
        for i in range(n):
            prod: int = 1
            for j in range(i, n):
                prod *= nums[j]
                ans = max(ans, prod)
        
        return ans

Complexity

  • ⏰ Time complexity: O(n^2) due to nested loops to generate all possible subarrays.
  • 🧺 Space complexity: O(1)

Method 2 - Algo Similar to Kadane’s Algo

This is similar to maximum subarray sum. Instead of sum, the sign of number affect the product value.

We can use maxEndingHere and minEndingHere to keep track of positive and negative product so far. But, wherever there is 0, our product becomes 0. So, we reset these 2 variables. We need to find the maximum product of these subarrays individually and return the largest product.

Inside this subproblem if there are even number of negative elements then maximum product is the complete product of the array. If there are odd number of negative elements, then make two subarrays once leaving the leftmost negative element and once leaving the rightmost negative element. The maximum of these two products will be returned.

Code

Java
public class Solution {
    public int maxProduct(int[] nums) {
        int n = nums.length;

        // Edge case: When the array is empty
        if (n == 0) {
            return 0;
        }

        // max positive product ending at the current position
        int maxEndingHere = nums[0]; 
        
        // min (negative) product ending at the current position
        int minEndingHere = nums[0]; 
        
        // Initialize overall max product to the first element
        int maxSoFar = nums[0];

        // Traverse through the array, starting from the second element
        for (int i = 1; i < n; i++) {
            int current = nums[i];
            
            // If the current element is negative, swap maxEndingHere and minEndingHere
            if (current < 0) {
                int temp = maxEndingHere;
                maxEndingHere = minEndingHere;
                minEndingHere = temp;
            }
            
            // Update maxEndingHere and minEndingHere
            maxEndingHere = Math.max(current, maxEndingHere * current);
            minEndingHere = Math.min(current, minEndingHere * current);

            // Update the global max product
            maxSoFar = Math.max(maxSoFar, maxEndingHere);
        }

        return maxSoFar;
    }
}

Complexity

  • ⏰ Time complexity: O(n)
  • 🧺 Space complexity: O(1)

Dry run

Consider the array nums = [1, -2, -3, 0, 7, -8, -2].

Initialization

nums[0] = 1, so maxEndingHere = 1 minEndingHere = 1 maxSoFar = 1

Iterations

Step 1 (i = 1, current = -2):

if (current < 0) { // current is -2
    int temp = maxEndingHere; // temp = 1
    maxEndingHere = minEndingHere; // maxEndingHere = 1
    minEndingHere = temp; // minEndingHere = 1
}
maxEndingHere = Math.max(-2, 1 * -2) = Math.max(-2, -2) = -2
minEndingHere = Math.min(-2, 1 * -2) = Math.min(-2, -2) = -2
maxSoFar = Math.max(1, -2) = 1

Step 2 (i = 2, current = -3):

if (current < 0) { // current is -3
    int temp = maxEndingHere; // temp = -2
    maxEndingHere = minEndingHere; // maxEndingHere = -2
    minEndingHere = temp; // minEndingHere = -2
}
maxEndingHere = Math.max(-3, -2 * -3) = Math.max(-3, 6) = 6
minEndingHere = Math.min(-3, -2 * -3) = Math.min(-3, 6) = -3
maxSoFar = Math.max(1, 6) = 6

Step 3 (i = 3, current = 0):

if (current < 0) { // current is 0 (skipped)
}
maxEndingHere = Math.max(0, 6 * 0) = Math.max(0, 0) = 0
minEndingHere = Math.min(0, -3 * 0) = Math.min(0, 0) = 0
maxSoFar = Math.max(6, 0) = 6

Step 4 (i = 4, current = 7):

if (current < 0) { // current is 7 (skipped)
}
maxEndingHere = Math.max(7, 0 * 7) = Math.max(7, 0) = 7
minEndingHere = Math.min(7, 0 * 7) = Math.min(7, 0) = 0
maxSoFar = Math.max(6, 7) = 7

Step 5 (i = 5, current = -8):

if (current < 0) { // current is -8
    int temp = maxEndingHere; // temp = 7
    maxEndingHere = minEndingHere; // maxEndingHere = 0
    minEndingHere = temp; // minEndingHere = 7
}
maxEndingHere = Math.max(-8, 0 * -8) = Math.max(-8, 0) = 0
minEndingHere = Math.min(-8, 7 * -8) = Math.min(-8, -56) = -56
maxSoFar = Math.max(7, 0) = 7

Step 6 (i = 6, current = -2):

if (current < 0) { // current is -2
    int temp = maxEndingHere; // temp = 0
    maxEndingHere = minEndingHere; // maxEndingHere = -56
    minEndingHere = temp; // minEndingHere = 0
}
maxEndingHere = Math.max(-2, -56 * -2) = Math.max(-2, 112) = 112
minEndingHere = Math.min(-2, 0 * -2) = Math.min(-2, 0) = -2
maxSoFar = Math.max(7, 112) = 112

Method 3 - Tracking max and min

At each index i, update the maximum and minimum values. Keeping track of the minimum is crucial because multiplying a negative number with the minimum value can turn it into the maximum, and vice versa. For each index i, calculate the maximum of the i-th element, prevMax multiplied by the i-th element, and prevMin multiplied by the i-th element.

Code

Java
class Solution {
	public int maxProduct(int nums[]) {	
		int max = nums[0], min = nums[0], ans = nums[0];
		
		for (int i = 1; i<n; i++) {
			int temp = max;  // store the max because before updating min your max will already be updated
			int num = nums[i];
			max = Math.max(Math.max(max * num, min * num), num);
            min = Math.min(Math.min(temp * num, min * num), num);
	
			ans = Math.max(ans, max);
		}
	
		return maxSoFar;
	}
}
Python
class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        # Initialising variables
        max_product: int = nums[0]
        min_product: int = nums[0]
        ans: int = nums[0]
        
        # Iterating through the array starting from index 1
        for num in nums[1:]:
            temp_max: int = max_product  # Store the current max before updating
            
            # Update max_product and min_product
            max_product = max(
	            num, num * max_product, num * min_product
	        )
            min_product = min(
	            num, num * temp_max, num * min_product
	        )
            
            # Update the final answer
            ans = max(ans, max_product)
        
        return ans