LeetCode 3736 - Minimum Moves to Equal Array Elements III
The problem asks us to determine the minimum number of moves required to make every element in an integer array equal. A move consists of selecting one element and increasing it by exactly 1. We are not allowed to decrease values, and we can only increment elements.
Difficulty: 🟢 Easy
Topics: Array, Math
Solution
Problem Understanding
The problem asks us to determine the minimum number of moves required to make every element in an integer array equal. A move consists of selecting one element and increasing it by exactly 1. We are not allowed to decrease values, and we can only increment elements.
In other words, we need to choose a target value such that every number in the array can be increased to that value using the fewest total operations. Since decreasing is not allowed, the target value must be at least as large as the largest element already present.
The input is an integer array nums, where each value represents an element that can be incremented independently. The output is a single integer representing the minimum total number of increment operations needed so that all values become identical.
The constraints are small:
1 <= nums.length <= 1001 <= nums[i] <= 100
These limits tell us that performance is not a major concern. Even less efficient approaches would work comfortably. However, the problem contains an important mathematical observation that allows us to solve it in a very clean and optimal way.
An important detail is that we can only increase elements. This restriction changes the strategy completely. If decreasing were allowed, we might choose something like the median, but here the only valid final value is the maximum element in the array. Choosing anything smaller is impossible because larger numbers cannot be reduced. Choosing anything larger would only add unnecessary operations.
Some important edge cases include arrays with only one element, arrays where all values are already equal, and arrays with a large gap between minimum and maximum values. The problem guarantees that the array is non-empty, so we never need to handle an invalid or empty input.
Approaches
Brute Force Approach
A brute force solution would try every possible target value and calculate how many moves are needed to make all elements equal to that target.
Since we can only increase numbers, the target must be at least the maximum element. We could try every target from max(nums) up to some upper bound, compute the cost for each candidate, and return the minimum.
For each candidate target, we would iterate through the array and sum:
$$target - nums[i]$$
for every element.
This works because it explicitly evaluates every possible valid ending state and chooses the smallest cost. However, it is unnecessary because targets larger than the maximum only increase the number of required moves. The minimum will always occur at the maximum value itself.
Although this approach is technically correct, it performs redundant work.
Optimal Approach
The key observation is that since we are only allowed to increase elements, the final equal value must be the maximum element already in the array.
Suppose the maximum element is M.
- Any value smaller than
Mis impossible becauseMcannot be decreased. - Any value larger than
Mis wasteful because every element, includingM, would need extra increments.
Therefore, the optimal target is exactly:
$$\text{target} = \max(nums)$$
Once we know the target, the answer becomes simple. For every element:
$$\text{moves} += target - num$$
The total sum of these differences gives the minimum required moves.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n × k) | O(1) | Tries multiple target values, redundant work |
| Optimal | O(n) | O(1) | Uses the maximum element as the only valid target |
Here, n is the length of the array and k represents the number of candidate target values checked in the brute force method.
Algorithm Walkthrough
- Find the maximum value in the array.
This value becomes the target because all elements can be increased to it, and any larger target would require unnecessary moves.
2. Initialize a variable total_moves = 0.
This variable will accumulate the total number of increments required. 3. Iterate through every number in the array.
For each value num, compute:
$$target - num$$
This tells us exactly how many increments are needed to raise num to the target.
4. Add the required increments to total_moves.
Repeat this for all elements.
5. Return total_moves.
After processing the entire array, the accumulated value represents the minimum number of moves needed.
Why it works
The correctness comes from the fact that only increment operations are allowed. Since we cannot reduce any value, the final equal number cannot be smaller than the maximum element. At the same time, choosing a value larger than the maximum introduces unnecessary increments for every element. Therefore, the maximum element is the unique optimal target, and summing the differences to this target gives the minimum possible move count.
Python Solution
from typing import List
class Solution:
def minMoves(self, nums: List[int]) -> int:
target = max(nums)
total_moves = 0
for num in nums:
total_moves += target - num
return total_moves
The implementation begins by finding the largest element in the array using max(nums). This value becomes the target that every element must reach.
Next, we initialize total_moves to track the total number of increment operations.
We then iterate through every number in the array. For each element, we compute how many increments are required to reach the target by subtracting the current value from the maximum value. This difference is added to the running total.
Finally, we return the accumulated number of moves.
The implementation directly mirrors the mathematical observation from the algorithm. Since each element independently needs target - num increments, summing these values naturally produces the minimum total.
Go Solution
func minMoves(nums []int) int {
target := nums[0]
for _, num := range nums {
if num > target {
target = num
}
}
totalMoves := 0
for _, num := range nums {
totalMoves += target - num
}
return totalMoves
}
The Go implementation follows the same logic as the Python solution. Since Go does not have a built-in max function for slices, we manually scan the array to find the maximum value.
After determining the target, we perform a second pass to calculate the total number of increments needed.
Because the constraints are very small and values are bounded by 100, integer overflow is not a concern. The input is guaranteed to contain at least one element, so accessing nums[0] is safe.
Worked Examples
Example 1
Input:
nums = [2, 1, 3]
First, find the maximum element:
target = 3
Now compute the required moves for each element.
| Index | Value | Target | Moves Needed | Running Total |
|---|---|---|---|---|
| 0 | 2 | 3 | 1 | 1 |
| 1 | 1 | 3 | 2 | 3 |
| 2 | 3 | 3 | 0 | 3 |
Final answer:
3
Example 2
Input:
nums = [4, 4, 5]
First, find the maximum element:
target = 5
Now calculate the increments needed.
| Index | Value | Target | Moves Needed | Running Total |
|---|---|---|---|---|
| 0 | 4 | 5 | 1 | 1 |
| 1 | 4 | 5 | 1 | 2 |
| 2 | 5 | 5 | 0 | 2 |
Final answer:
2
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | One pass to find the maximum and one pass to compute moves |
| Space | O(1) | Only a few variables are used |
The algorithm runs in linear time because each element is processed a constant number of times. We first determine the maximum value, then iterate again to compute the required increments. No additional data structures proportional to input size are used, so the space complexity remains constant.
Test Cases
solution = Solution()
assert solution.minMoves([2, 1, 3]) == 3 # Example 1
assert solution.minMoves([4, 4, 5]) == 2 # Example 2
assert solution.minMoves([7]) == 0 # Single element array
assert solution.minMoves([5, 5, 5]) == 0 # Already equal
assert solution.minMoves([1, 100]) == 99 # Large difference between values
assert solution.minMoves([1, 2, 3, 4]) == 6 # Increasing sequence
assert solution.minMoves([10, 1, 1]) == 18 # One large maximum
assert solution.minMoves([100, 100, 100, 100]) == 0 # All max values
assert solution.minMoves([1, 1, 100]) == 198 # Extreme gap case
assert solution.minMoves([3, 2, 2, 3]) == 2 # Mixed duplicates
| Test | Why |
|---|---|
[2,1,3] |
Validates the first provided example |
[4,4,5] |
Validates the second provided example |
[7] |
Tests smallest valid input |
[5,5,5] |
Ensures zero moves when already equal |
[1,100] |
Tests large value gap |
[1,2,3,4] |
Verifies accumulation across multiple elements |
[10,1,1] |
Ensures correct handling of one dominant maximum |
[100,100,100,100] |
Confirms no unnecessary moves |
[1,1,100] |
Tests extreme difference scenario |
[3,2,2,3] |
Validates handling of duplicates |
Edge Cases
Single Element Array
An array with only one element, such as [7], requires zero moves because all elements are already equal. A naive implementation might unnecessarily process or modify the value, but this solution naturally returns 0 because the maximum equals the element itself.
All Elements Already Equal
For an input like [5, 5, 5], every element already matches the maximum value. Each subtraction becomes:
5 - 5 = 0
The accumulated total remains zero, ensuring no unnecessary operations are counted.
Large Difference Between Minimum and Maximum
Cases such as [1, 1, 100] can expose arithmetic mistakes in implementations that incorrectly choose the target value. If the target were chosen larger than the maximum, the answer would be inflated. This implementation correctly uses 100 as the target and computes:
(100 - 1) + (100 - 1) + (100 - 100)
= 99 + 99 + 0
= 198
which is the true minimum.