Facebook Pixel

1509. Minimum Difference Between Largest and Smallest Value in Three Moves

Problem Description

You have an integer array nums. You can perform a move by selecting any element in the array and changing it to any value you want.

Your goal is to minimize the difference between the maximum and minimum values in the array after performing at most 3 moves.

For example, if you have an array like [5, 3, 2, 4], after making up to 3 moves, you could change some elements to make the array have a smaller range between its largest and smallest values.

The problem asks you to return the minimum possible difference between the largest and smallest values after these modifications.

Key constraints:

  • You can make 0, 1, 2, or 3 moves (but no more than 3)
  • Each move allows you to change one element to any value
  • You want to minimize max(nums) - min(nums) after the moves

Special case: If the array has 4 or fewer elements, you can change up to 3 of them, which means you can make all elements equal (resulting in a difference of 0).

The solution works by sorting the array and considering different combinations of removing elements from the beginning and end. Since we can change up to 3 elements, we effectively consider scenarios where we "remove" 0-3 elements from the left and 3-0 elements from the right, finding the minimum difference among all these combinations.

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

Intuition

Think about what happens when we can change up to 3 elements in the array. The key insight is that to minimize the difference between max and min, we should target the extreme values - either the largest elements or the smallest elements, or a combination of both.

Why? Because changing middle values doesn't help us reduce the range. If we have a sorted array like [1, 2, 3, 4, 100], changing the 3 to any value won't reduce the difference between 100 and 1. We need to deal with the extremes.

Since we can make up to 3 changes, we can effectively "remove" up to 3 problematic extreme values by changing them to values within our desired range. After sorting the array, these extreme values are at the ends.

Here's the critical realization: if we have n elements and can change 3 of them, we're essentially choosing which n-3 consecutive elements to keep from the sorted array. The difference will be determined by the range of these kept elements.

We have exactly 4 ways to distribute our 3 changes:

  • Change 0 from left, 3 from right: keep elements from nums[0] to nums[n-4]
  • Change 1 from left, 2 from right: keep elements from nums[1] to nums[n-3]
  • Change 2 from left, 1 from right: keep elements from nums[2] to nums[n-2]
  • Change 3 from left, 0 from right: keep elements from nums[3] to nums[n-1]

For each scenario, the minimum difference is the range of the kept elements. We try all 4 combinations and take the minimum.

When the array has 4 or fewer elements, we can change enough elements (3 out of 4 maximum) to make them all equal, giving us a difference of 0.

Learn more about Greedy and Sorting patterns.

Solution Approach

The implementation follows a straightforward approach based on our intuition:

Step 1: Handle Edge Case

if n < 5:
    return 0

When we have 4 or fewer elements, we can change up to 3 of them to make all elements equal, resulting in a difference of 0.

Step 2: Sort the Array

nums.sort()

Sorting allows us to easily identify and work with the extreme values at both ends of the array.

Step 3: Try All Possible Distributions

ans = inf
for l in range(4):
    r = 3 - l
    ans = min(ans, nums[n - 1 - r] - nums[l])

We iterate through all 4 possible ways to distribute our 3 changes:

  • When l = 0: Remove 0 from left, 3 from right โ†’ difference is nums[n-4] - nums[0]
  • When l = 1: Remove 1 from left, 2 from right โ†’ difference is nums[n-3] - nums[1]
  • When l = 2: Remove 2 from left, 1 from right โ†’ difference is nums[n-2] - nums[2]
  • When l = 3: Remove 3 from left, 0 from right โ†’ difference is nums[n-1] - nums[3]

The formula nums[n - 1 - r] - nums[l] calculates the range of elements we're keeping. Since r = 3 - l, when we skip l elements from the left and r elements from the right, our new maximum is at index n - 1 - r and our new minimum is at index l.

Step 4: Return the Minimum We track the minimum difference across all combinations and return it.

Time Complexity: O(n log n) due to sorting Space Complexity: O(1) if we don't count the sorting space, otherwise O(log n) for the sorting algorithm's stack space

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with the array nums = [1, 5, 0, 10, 14].

Step 1: Check edge case

  • Array has 5 elements (n = 5), which is โ‰ฅ 5, so we continue.

Step 2: Sort the array

  • After sorting: nums = [0, 1, 5, 10, 14]

Step 3: Try all 4 distributions of our 3 moves

We can change up to 3 elements. Let's examine each way to distribute these changes:

Option 1 (l=0, r=3): Change 0 from left, 3 from right

  • Keep elements from index 0 to index 1 (n-1-r = 5-1-3 = 1)
  • Keep: [0, 1]
  • Difference: nums[1] - nums[0] = 1 - 0 = 1

Option 2 (l=1, r=2): Change 1 from left, 2 from right

  • Keep elements from index 1 to index 2 (n-1-r = 5-1-2 = 2)
  • Keep: [1, 5]
  • Difference: nums[2] - nums[1] = 5 - 1 = 4

Option 3 (l=2, r=1): Change 2 from left, 1 from right

  • Keep elements from index 2 to index 3 (n-1-r = 5-1-1 = 3)
  • Keep: [5, 10]
  • Difference: nums[3] - nums[2] = 10 - 5 = 5

Option 4 (l=3, r=0): Change 3 from left, 0 from right

  • Keep elements from index 3 to index 4 (n-1-r = 5-1-0 = 4)
  • Keep: [10, 14]
  • Difference: nums[4] - nums[3] = 14 - 10 = 4

Step 4: Return minimum

  • Minimum of [1, 4, 5, 4] = 1

The answer is 1. This makes sense because if we change the three largest values (5, 10, 14) to values between 0 and 1, our array could become something like [0, 1, 1, 1, 1], giving us a range of 1 - 0 = 1.

Solution Implementation

1from typing import List
2from math import inf
3
4
5class Solution:
6    def minDifference(self, nums: List[int]) -> int:
7        """
8        Find the minimum difference between max and min values after changing at most 3 elements.
9      
10        Strategy: After sorting, we can remove up to 3 elements from either end.
11        We try all combinations: remove 0-3 from left and 3-0 from right.
12      
13        Args:
14            nums: List of integers
15          
16        Returns:
17            Minimum possible difference between max and min after changes
18        """
19        n = len(nums)
20      
21        # If we have 4 or fewer elements, we can change all to be equal
22        if n < 5:
23            return 0
24      
25        # Sort array to easily access smallest and largest elements
26        nums.sort()
27      
28        # Initialize minimum difference to infinity
29        min_difference = inf
30      
31        # Try all combinations of removing elements from left and right
32        # left_remove: number of smallest elements to remove (0 to 3)
33        # right_remove: number of largest elements to remove (3 to 0)
34        for left_remove in range(4):
35            right_remove = 3 - left_remove
36          
37            # Calculate difference between the new max and new min
38            # New min: nums[left_remove] (after removing left_remove smallest)
39            # New max: nums[n - 1 - right_remove] (after removing right_remove largest)
40            current_difference = nums[n - 1 - right_remove] - nums[left_remove]
41          
42            # Update minimum difference
43            min_difference = min(min_difference, current_difference)
44      
45        return min_difference
46
1class Solution {
2    public int minDifference(int[] nums) {
3        int arrayLength = nums.length;
4      
5        // If array has 4 or fewer elements, we can change all elements to be equal
6        // Therefore, minimum difference would be 0
7        if (arrayLength < 5) {
8            return 0;
9        }
10      
11        // Sort the array to easily identify smallest and largest elements
12        Arrays.sort(nums);
13      
14        // Initialize minimum difference with a large value
15        long minimumDifference = Long.MAX_VALUE;
16      
17        // Try all possible combinations of removing elements from left and right
18        // We can change at most 3 elements, so we have 4 scenarios:
19        // - Change 0 from left, 3 from right
20        // - Change 1 from left, 2 from right  
21        // - Change 2 from left, 1 from right
22        // - Change 3 from left, 0 from right
23        for (int leftChanges = 0; leftChanges <= 3; leftChanges++) {
24            int rightChanges = 3 - leftChanges;
25          
26            // Calculate difference between the remaining largest and smallest elements
27            // after changing leftChanges smallest and rightChanges largest elements
28            long currentDifference = (long) nums[arrayLength - 1 - rightChanges] - nums[leftChanges];
29          
30            // Update minimum difference if current is smaller
31            minimumDifference = Math.min(minimumDifference, currentDifference);
32        }
33      
34        return (int) minimumDifference;
35    }
36}
37
1class Solution {
2public:
3    int minDifference(vector<int>& nums) {
4        int arraySize = nums.size();
5      
6        // If array has 4 or fewer elements, we can change up to 3 values
7        // to make all elements equal, resulting in 0 difference
8        if (arraySize < 5) {
9            return 0;
10        }
11      
12        // Sort the array to easily identify minimum and maximum values
13        sort(nums.begin(), nums.end());
14      
15        // Initialize result with a large value (2^60)
16        long long minDiff = 1LL << 60;
17      
18        // Try all possible combinations of removing elements from left and right
19        // We can change at most 3 elements total
20        for (int leftChanges = 0; leftChanges <= 3; ++leftChanges) {
21            // Calculate how many elements to change from the right side
22            int rightChanges = 3 - leftChanges;
23          
24            // Calculate the difference between the new maximum and minimum
25            // after changing 'leftChanges' smallest and 'rightChanges' largest elements
26            long long currentDiff = static_cast<long long>(nums[arraySize - 1 - rightChanges]) - 
27                                   static_cast<long long>(nums[leftChanges]);
28          
29            // Update minimum difference
30            minDiff = min(minDiff, currentDiff);
31        }
32      
33        return static_cast<int>(minDiff);
34    }
35};
36
1function minDifference(nums: number[]): number {
2    // If array has 4 or fewer elements, we can remove all but one element
3    // making the difference 0
4    if (nums.length < 5) {
5        return 0;
6    }
7  
8    // Sort the array in ascending order
9    nums.sort((a, b) => a - b);
10  
11    // Initialize minimum difference to positive infinity
12    let minDiff: number = Number.POSITIVE_INFINITY;
13  
14    // Try all 4 possible ways to remove 3 elements:
15    // - Remove 0 from start and 3 from end: nums[n-4] - nums[0]
16    // - Remove 1 from start and 2 from end: nums[n-3] - nums[1]
17    // - Remove 2 from start and 1 from end: nums[n-2] - nums[2]
18    // - Remove 3 from start and 0 from end: nums[n-1] - nums[3]
19    for (let i: number = 0; i < 4; i++) {
20        // Use at() with negative index to access from the end
21        // i - 4 gives us -4, -3, -2, -1 for i = 0, 1, 2, 3
22        const endValue: number = nums.at(i - 4)!;
23        const startValue: number = nums[i];
24        minDiff = Math.min(minDiff, endValue - startValue);
25    }
26  
27    return minDiff;
28}
29

Time and Space Complexity

Time Complexity: O(n log n) where n is the length of the input array nums.

The dominant operation in this algorithm is the sorting step nums.sort(), which takes O(n log n) time. After sorting, the code performs a constant number of operations - specifically, it iterates exactly 4 times in the for loop (from l = 0 to l = 3), and in each iteration, it performs constant-time operations (array access and min comparison). Therefore, the post-sorting operations take O(1) time. The overall time complexity is O(n log n) + O(1) = O(n log n).

Space Complexity: O(1) or O(n) depending on the sorting algorithm implementation.

If the built-in sort() method uses an in-place sorting algorithm like heapsort, the space complexity would be O(1) as we only use a constant amount of extra space for variables n, ans, l, and r. However, Python's Timsort (the algorithm used by the built-in sort()) has a worst-case space complexity of O(n) for the temporary arrays it uses during the sorting process. Since we're sorting the input array in-place and not creating any additional data structures proportional to the input size (apart from what the sorting algorithm uses internally), the space complexity is determined by the sorting algorithm's requirements.

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

Common Pitfalls

1. Forgetting to Handle Small Arrays

One of the most common mistakes is not properly handling arrays with 4 or fewer elements. Without this check, the code will still work but miss the optimization opportunity.

Pitfall Example:

def minDifference(self, nums: List[int]) -> int:
    nums.sort()
    n = len(nums)
    min_diff = inf
  
    # This will cause index errors for arrays with less than 4 elements!
    for l in range(4):
        r = 3 - l
        min_diff = min(min_diff, nums[n - 1 - r] - nums[l])
  
    return min_diff

Why it fails: For an array like [1, 2], when l = 3, we try to access nums[3] which doesn't exist.

Solution: Always check array length first:

if len(nums) < 5:
    return 0

2. Misunderstanding the Problem - Thinking We Need to Actually Change Values

Some might overthink and try to determine what values to change elements to, rather than realizing we just need to "ignore" the extreme values.

Pitfall Example:

def minDifference(self, nums: List[int]) -> int:
    # Wrong approach: trying to find optimal values to change to
    nums.sort()
    median = nums[len(nums) // 2]
  
    # Changing closest 3 elements to median - INCORRECT!
    # This doesn't guarantee minimum difference

Solution: Understand that changing an element optimally means either:

  • Making it equal to the minimum (if it was large)
  • Making it equal to the maximum (if it was small)
  • Making it something in between (which is equivalent to ignoring it)

Therefore, we only need to consider which elements to "remove" from consideration.

3. Off-by-One Errors in Index Calculation

When calculating the new maximum index after removing elements from the right, it's easy to make indexing mistakes.

Pitfall Example:

for l in range(4):
    r = 3 - l
    # Wrong: should be n - 1 - r, not n - r
    min_diff = min(min_diff, nums[n - r] - nums[l])

Why it fails: nums[n - r] when r = 0 would access nums[n], which is out of bounds.

Solution: Remember that the last element is at index n - 1, so after removing r elements from the right, the new maximum is at n - 1 - r.

4. Not Considering All Combinations

Some might think we should always remove from both ends equally or follow some other pattern.

Pitfall Example:

def minDifference(self, nums: List[int]) -> int:
    nums.sort()
    n = len(nums)
  
    # Wrong: Only considering removing from both ends equally
    return min(
        nums[-1] - nums[3],  # Remove 3 from left only
        nums[-4] - nums[0]   # Remove 3 from right only
    )
    # Missing cases like removing 1 from left and 2 from right

Solution: Always iterate through all 4 possible distributions (0-3, 1-2, 2-1, 3-0) to find the true minimum.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following array represent a max heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More