Facebook Pixel

462. Minimum Moves to Equal Array Elements II

Problem Description

You are given an integer array nums of size n. Your goal is to make all elements in the array equal by performing a series of moves. In each move, you can either increment or decrement any element of the array by 1.

For example, if you have an array [1, 2, 3], you could:

  • Increment the first element by 1 to get [2, 2, 3]
  • Decrement the third element by 1 to get [2, 2, 2]
  • This takes a total of 2 moves to make all elements equal to 2

The problem asks you to find the minimum number of moves required to make all array elements equal. You need to determine:

  1. What value all elements should become (though you don't need to output this value)
  2. The total minimum number of increment/decrement operations needed

The key insight is that the optimal target value is the median of the array. When all elements need to converge to a single value, choosing the median minimizes the total distance (number of moves) that all elements need to travel. This is because the median has the property that the sum of absolute deviations from it is minimized compared to any other value.

The solution sorts the array to find the median efficiently, then calculates the sum of absolute differences between each element and the median, which gives the minimum number of moves required.

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

Intuition

To understand why we should choose the median, let's think about what happens when we pick different target values.

Imagine the numbers plotted on a number line. When we choose a target value k, each number needs to move to k, and the cost is the distance it travels. The total cost is the sum of all these distances: Σ|nums[i] - k|.

Let's consider a simple example with just two numbers: [1, 5].

  • If we choose k = 1, the cost is |1-1| + |5-1| = 0 + 4 = 4
  • If we choose k = 3, the cost is |1-3| + |5-3| = 2 + 2 = 4
  • If we choose k = 5, the cost is |1-5| + |5-5| = 4 + 0 = 4

Notice that any value between 1 and 5 gives the same total cost! This is because as we move the target from left to right, one number gets closer (saving moves) while the other gets farther (costing moves) by the same amount.

Now extend this to multiple numbers. When we have an odd number of elements, the median is the middle element after sorting. Why is this optimal?

Consider sorted array [1, 3, 8]. If we choose the median 3:

  • Numbers to the left of 3 need to move right
  • Numbers to the right of 3 need to move left
  • There are equal numbers on each side (or differ by at most 1)

If we move the target away from the median:

  • Moving right: All numbers on the left (including the median) need to move 1 extra step right, but only the numbers on the right save 1 step. Since there are at least as many numbers on the left, we don't save moves overall.
  • Moving left: Similar reasoning applies.

For even-sized arrays, any value between the two middle elements works equally well, so we can pick either one as our median.

This geometric interpretation shows that the median minimizes the sum of absolute deviations, making it the optimal choice for our target value.

Solution Approach

The implementation follows a straightforward approach based on finding the median and calculating the total distance:

Step 1: Sort the array

nums.sort()

We sort the array in ascending order. This allows us to easily find the median and is necessary for the median calculation. Sorting takes O(n log n) time.

Step 2: Find the median

k = nums[len(nums) >> 1]

After sorting, the median is simply the middle element. The expression len(nums) >> 1 is a bitwise right shift operation, which is equivalent to len(nums) // 2 (integer division by 2). This gives us:

  • For odd-length arrays: The exact middle element
  • For even-length arrays: The second of the two middle elements (though either middle element would work)

For example:

  • [1, 2, 3, 4, 5] → index 2 → median = 3
  • [1, 2, 3, 4] → index 2 → median = 3 (either 2 or 3 would work)

Step 3: Calculate the minimum moves

return sum(abs(v - k) for v in nums)

Once we have the median k, we calculate the sum of absolute differences between each element and the median. This represents the total number of moves needed:

  • abs(v - k) gives the number of moves needed to change element v to k
  • The sum aggregates all individual moves into the total minimum moves

Example walkthrough: For nums = [1, 2, 3]:

  1. Array is already sorted: [1, 2, 3]
  2. Median: k = nums[3 >> 1] = nums[1] = 2
  3. Total moves: |1-2| + |2-2| + |3-2| = 1 + 0 + 1 = 2

Time Complexity: O(n log n) due to sorting Space Complexity: O(log n) for the sorting algorithm's recursive stack space (in Python's Timsort)

The elegance of this solution lies in its simplicity - by leveraging the mathematical property that the median minimizes the sum of absolute deviations, we reduce the problem to just sorting and a simple calculation.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through the solution with the array nums = [1, 10, 2, 9].

Step 1: Sort the array

  • Original: [1, 10, 2, 9]
  • After sorting: [1, 2, 9, 10]

Step 2: Find the median

  • Array length is 4 (even), so we take the element at index 4 >> 1 = 2
  • Median k = nums[2] = 9
  • Note: We could also choose nums[1] = 2 as the median for even-length arrays, and the total moves would be the same

Step 3: Calculate moves for each element

  • Element 1 → 9: |1 - 9| = 8 moves
  • Element 2 → 9: |2 - 9| = 7 moves
  • Element 9 → 9: |9 - 9| = 0 moves
  • Element 10 → 9: |10 - 9| = 1 move

Total moves: 8 + 7 + 0 + 1 = 16

To verify this is optimal, let's check what happens if we chose a different target:

  • If we chose 2 as target: |1-2| + |2-2| + |9-2| + |10-2| = 1 + 0 + 7 + 8 = 16 (same!)
  • If we chose 5 as target: |1-5| + |2-5| + |9-5| + |10-5| = 4 + 3 + 4 + 5 = 16 (still the same!)
  • If we chose 1 as target: |1-1| + |2-1| + |9-1| + |10-1| = 0 + 1 + 8 + 9 = 18 (worse)
  • If we chose 10 as target: |1-10| + |2-10| + |9-10| + |10-10| = 9 + 8 + 1 + 0 = 18 (worse)

This confirms that choosing any value between the two middle elements (2 and 9) gives us the optimal result of 16 moves, while moving outside this range increases the total moves needed.

Solution Implementation

1class Solution:
2    def minMoves2(self, nums: List[int]) -> int:
3        """
4        Find minimum number of moves to make all array elements equal.
5        Each move consists of incrementing or decrementing an element by 1.
6      
7        The optimal strategy is to change all elements to the median value,
8        as the median minimizes the sum of absolute deviations.
9      
10        Args:
11            nums: List of integers
12          
13        Returns:
14            Minimum total number of moves required
15        """
16        # Sort the array to find the median
17        nums.sort()
18      
19        # Find the median element (middle element for odd length, 
20        # or either middle element for even length)
21        # Using bit shift (>> 1) is equivalent to integer division by 2
22        median = nums[len(nums) >> 1]
23      
24        # Calculate total moves as sum of absolute differences from median
25        # Each absolute difference represents the number of moves for that element
26        total_moves = sum(abs(num - median) for num in nums)
27      
28        return total_moves
29
1class Solution {
2    public int minMoves2(int[] nums) {
3        // Sort the array to find the median
4        Arrays.sort(nums);
5      
6        // Get the median value (middle element for odd length, right-middle for even length)
7        // Using bit shift for division by 2
8        int median = nums[nums.length >> 1];
9      
10        // Calculate total moves needed
11        int totalMoves = 0;
12      
13        // Sum up the absolute differences between each element and the median
14        for (int num : nums) {
15            totalMoves += Math.abs(num - median);
16        }
17      
18        return totalMoves;
19    }
20}
21
1class Solution {
2public:
3    int minMoves2(vector<int>& nums) {
4        // Sort the array to find the median
5        sort(nums.begin(), nums.end());
6      
7        // Get the median element (middle element for odd size, upper middle for even size)
8        int median = nums[nums.size() / 2];
9      
10        // Calculate the total number of moves needed
11        int totalMoves = 0;
12      
13        // Sum up the absolute differences between each element and the median
14        for (int& num : nums) {
15            totalMoves += abs(num - median);
16        }
17      
18        return totalMoves;
19    }
20};
21
1/**
2 * Finds the minimum number of moves to make all array elements equal,
3 * where a move consists of incrementing or decrementing an element by 1.
4 * Uses the median as the optimal meeting point.
5 * 
6 * @param nums - Array of integers to be equalized
7 * @returns The minimum number of moves required
8 */
9function minMoves2(nums: number[]): number {
10    // Sort the array in ascending order to find the median
11    nums.sort((a: number, b: number) => a - b);
12  
13    // Find the median value (middle element for optimal moves)
14    // Using bit shift for integer division by 2
15    const medianValue: number = nums[nums.length >> 1];
16  
17    // Calculate total moves by summing absolute differences from median
18    const totalMoves: number = nums.reduce(
19        (accumulator: number, currentValue: number) => {
20            return accumulator + Math.abs(currentValue - medianValue);
21        }, 
22        0
23    );
24  
25    return totalMoves;
26}
27

Time and Space Complexity

Time Complexity: O(n log n)

The time complexity is dominated by the sorting operation nums.sort(), which takes O(n log n) time where n is the length of the input array. After sorting:

  • Finding the median by accessing nums[len(nums) >> 1] takes O(1) time
  • The sum comprehension sum(abs(v - k) for v in nums) iterates through all n elements once, taking O(n) time

Therefore, the overall time complexity is O(n log n) + O(1) + O(n) = O(n log n).

Space Complexity: O(1) or O(n)

The space complexity depends on the implementation of Python's sort:

  • If we consider the sorting to be in-place (Python's Timsort uses O(1) auxiliary space in the best case for already sorted arrays), the space complexity is O(1) as we only use a constant amount of extra space for the variable k and the generator expression in sum().
  • However, Python's Timsort can use up to O(n) auxiliary space in the worst case for the temporary arrays used during merge operations.

Most commonly, this is analyzed as O(1) space complexity when considering only the extra space used beyond the input, since the sort modifies the input array in-place.

Common Pitfalls

1. Confusing with the Mean Instead of Median

A common mistake is thinking that the optimal target value should be the average (mean) of all elements rather than the median. This seems intuitive but is mathematically incorrect.

Why this happens: The mean minimizes the sum of squared differences, not the sum of absolute differences.

Example where mean fails:

nums = [1, 2, 100]
mean = (1 + 2 + 100) / 334.33round to 34
moves_to_mean = |1-34| + |2-34| + |100-34| = 33 + 32 + 66 = 131

median = 2
moves_to_median = |1-2| + |2-2| + |100-2| = 1 + 0 + 98 = 99

The median gives 99 moves while using the mean would give 131 moves.

Solution: Always use the median for minimizing sum of absolute deviations.

2. Incorrect Median Calculation for Even-Length Arrays

When the array has even length, there are two middle elements. Some might try to average them or use complex logic.

Incorrect approach:

# Trying to average the two middle elements
if len(nums) % 2 == 0:
    median = (nums[len(nums)//2 - 1] + nums[len(nums)//2]) // 2

Why it's unnecessary: For this problem, either of the two middle elements will give the same minimum number of moves. The implementation correctly uses nums[len(nums) >> 1] which picks one of them.

Proof: For even-length sorted arrays, any value between (and including) the two middle elements yields the same total distance.

3. Forgetting to Sort the Array

Attempting to find the median without sorting leads to incorrect results.

Wrong approach:

def minMoves2(self, nums):
    # Directly taking middle element without sorting
    median = nums[len(nums) // 2]  # Wrong!
    return sum(abs(v - median) for v in nums)

Solution: Always sort first: nums.sort()

4. Integer Overflow Concerns (Language-Specific)

In languages with fixed integer sizes (like Java or C++), calculating the sum of moves might cause overflow for large arrays with large values.

Python advantage: Python handles arbitrary precision integers automatically, so this isn't a concern here.

For other languages: Use appropriate data types (e.g., long long in C++ or long in Java).

5. Modifying Original Array Without Consideration

The solution sorts the input array in-place, which modifies it. If the original order needs to be preserved elsewhere in your code, this could cause issues.

Solution if preservation needed:

def minMoves2(self, nums):
    sorted_nums = sorted(nums)  # Creates a new sorted list
    median = sorted_nums[len(sorted_nums) >> 1]
    return sum(abs(v - median) for v in nums)

This uses sorted() instead of sort() to create a new sorted list while preserving the original.

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

Which two pointer techniques do you use to check if a string is a palindrome?


Recommended Readings

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

Load More