LeetCode 2016 - Maximum Difference Between Increasing Elements

The problem is asking us to find the maximum difference between two elements in an array, with the condition that the smaller element appears before the larger element.

LeetCode Problem 2016

Difficulty: 🟢 Easy
Topics: Array

Solution

Problem Understanding

The problem is asking us to find the maximum difference between two elements in an array, with the condition that the smaller element appears before the larger element. Formally, given a 0-indexed array nums of length n, we need to find the largest value of nums[j] - nums[i] where i < j and nums[i] < nums[j]. If no such pair exists, we return -1.

The input represents an integer array where each element is guaranteed to be at least 1 and at most 10^9. The array has at least two elements and at most 1000 elements, which is small enough that an O(n^2) solution may technically work, but an O(n) solution is preferable. The expected output is a single integer, either the maximum valid difference or -1.

Important edge cases include arrays that are strictly decreasing (where no valid pair exists), arrays where the maximum difference is at the last element minus the first element, and arrays where multiple pairs have the same maximum difference.

Approaches

The brute-force approach iterates through all possible pairs (i, j) where i < j. For each pair, we check if nums[i] < nums[j] and calculate the difference. We then keep track of the largest difference encountered. This is correct because it explicitly evaluates every valid pair, but it is inefficient for larger arrays due to its O(n^2) time complexity.

The optimal approach relies on the key insight that to maximize nums[j] - nums[i] with i < j, we only need to track the minimum value seen so far as we iterate through the array. For each element nums[j], the maximum difference using that element is nums[j] - min_value_so_far. We update the maximum difference whenever this value exceeds the current maximum, and we also update the min_value_so_far if nums[j] is smaller than the current minimum. This works in O(n) time with O(1) space.

Approach Time Complexity Space Complexity Notes
Brute Force O(n^2) O(1) Iterate over all pairs (i, j) and compute differences
Optimal O(n) O(1) Track minimum value so far and compute differences on the fly

Algorithm Walkthrough

  1. Initialize two variables: min_value to the first element of the array and max_diff to -1. min_value will track the smallest element seen so far, ensuring i < j. max_diff will store the maximum difference found that satisfies nums[i] < nums[j].
  2. Iterate through the array starting from index 1 (since index 0 is our initial minimum).
  3. For each element nums[j], if nums[j] is greater than min_value, calculate the difference nums[j] - min_value and update max_diff if this difference is larger than the current max_diff.
  4. Regardless of the comparison, update min_value if nums[j] is smaller than the current min_value.
  5. Continue this process until the end of the array.
  6. Return max_diff, which will either be the maximum difference found or -1 if no valid pair exists.

Why it works: At each step, min_value guarantees that the candidate difference is computed using a previous element that occurs before the current one. This ensures i < j. By iterating through the array once, we consider all valid pairs indirectly and track the largest difference, giving the correct result efficiently.

Python Solution

from typing import List

class Solution:
    def maximumDifference(self, nums: List[int]) -> int:
        min_value = nums[0]
        max_diff = -1
        
        for num in nums[1:]:
            if num > min_value:
                max_diff = max(max_diff, num - min_value)
            min_value = min(min_value, num)
        
        return max_diff

The implementation starts by initializing min_value to the first element and max_diff to -1. We iterate through the array starting from the second element. For each element, we check if it is larger than min_value. If so, we update max_diff. Regardless, we update min_value to ensure it always represents the smallest element seen so far. This follows the optimal algorithm described above.

Go Solution

func maximumDifference(nums []int) int {
    minValue := nums[0]
    maxDiff := -1
    
    for _, num := range nums[1:] {
        if num > minValue {
            diff := num - minValue
            if diff > maxDiff {
                maxDiff = diff
            }
        }
        if num < minValue {
            minValue = num
        }
    }
    
    return maxDiff
}

In Go, we follow the same logic. We initialize minValue to the first element and maxDiff to -1. Iterating through the slice from index 1, we update maxDiff whenever a larger valid difference is found and update minValue if the current number is smaller. There is no need for additional data structures, and we handle large integers natively.

Worked Examples

Example 1: nums = [7,1,5,4]

Index num min_value num > min_value? max_diff
0 7 7 - -1
1 1 1 No -1
2 5 1 Yes 4
3 4 1 Yes 4

Return 4.

Example 2: nums = [9,4,3,2]

Index num min_value num > min_value? max_diff
0 9 9 - -1
1 4 4 No -1
2 3 3 No -1
3 2 2 No -1

Return -1.

Example 3: nums = [1,5,2,10]

Index num min_value num > min_value? max_diff
0 1 1 - -1
1 5 1 Yes 4
2 2 1 Yes 4
3 10 1 Yes 9

Return 9.

Complexity Analysis

Measure Complexity Explanation
Time O(n) Single pass through the array, comparing each element with min_value
Space O(1) Only two extra variables are used, regardless of input size

The optimal solution achieves linear time because it only requires tracking the minimum seen so far and updating the maximum difference on the fly. No additional data structures or nested loops are necessary, resulting in constant space usage.

Test Cases

# Provided examples
assert Solution().maximumDifference([7,1,5,4]) == 4  # normal case
assert Solution().maximumDifference([9,4,3,2]) == -1  # strictly decreasing
assert Solution().maximumDifference([1,5,2,10]) == 9  # maximum difference at last element

# Edge cases
assert Solution().maximumDifference([1,2]) == 1  # smallest possible array
assert Solution().maximumDifference([2,1]) == -1  # two elements decreasing
assert Solution().maximumDifference([5,5,5,5]) == -1  # all elements equal
assert Solution().maximumDifference([1,2,3,4,5]) == 4  # strictly increasing
assert Solution().maximumDifference([10,1,10,1,10]) == 9  # alternating high-low values
Test Why
[7,1,5,4] normal case with valid difference
[9,4,3,2] strictly decreasing, no valid pair
[1,5,2,10] maximum difference occurs at last element
[1,2] minimal size array with valid difference
[2,1] minimal size array with no valid difference
[5,5,5,5] all elements equal, no valid difference
[1,2,3,4,5] strictly increasing, max difference is last-first
[10,1,10,1,10] multiple peaks, ensure algorithm finds largest difference

Edge Cases

One important edge case is when the array is strictly decreasing, such as [9,4,3,2]. Here, there is no valid pair where nums[i] < nums[j]. The algorithm handles this correctly by initializing max_diff to -1 and never finding a valid difference.

Another edge case is when all elements are equal, e.g., [5,5,5,5]. In this situation, the condition nums[i] < nums[j] is never satisfied. Again, the initialization of max_diff to `-1