1675. Minimize Deviation in Array - Explanation
Description
You may repeatedly change any entry of the positive integer array nums: divide an even value by 2, or multiply an odd value by 2. Choose any entries and perform any number of operations, including none. The deviation is the largest array value minus the smallest. Return the smallest deviation you can achieve.
Example 1:
Input: nums = [3,6]
Output: 0Explanation: Divide 6 by 2 to obtain [3,3]. Both values are equal, so the deviation is 0.
Example 2:
Input: nums = [3,5]
Output: 1Explanation: The first entry can be 3 or 6, and the second can be 5 or 10. Choosing [6,5] gives deviation 1, the smallest possible.
Example 3:
Input: nums = [8,4,2]
Output: 0Explanation: Divide 8 by 2 twice and divide 4 by 2 once. The array becomes [2,2,2], with deviation 0.
Constraints:
2 <= nums.length <= 500001 <= nums[i] <= 1000000000
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Heap / Priority Queue - Using min-heap or max-heap to efficiently track extreme values
- Sliding Window - Maintaining a valid window while tracking which elements are represented
- Sorting - Sorting pairs of values and using sorted order to find optimal ranges
- Bit Manipulation - Understanding odd/even properties and operations like doubling and halving
1. Sorting + Sliding Window
Intuition
Each element in the array can only take certain values: odd numbers can become themselves or double themselves, while even numbers can be halved repeatedly until they become odd. The key insight is that we can precompute all possible values each element can take, then find the smallest range that contains at least one value from each original element.
This transforms the problem into finding the smallest window in a sorted list of values where each original array element is represented at least once. We use a sliding window approach on the sorted list of all possible values, tracking which original elements are covered.
Algorithm
- For each number in the input array, generate all possible values it can take:
- If odd: it can be itself or doubled (only once).
- If even: it can be halved repeatedly down to an odd number, plus all intermediate values.
- Store each value paired with its original index.
- Sort all these pairs by value.
- Use a sliding window with two pointers
iandj:- Expand
jto include more values and track how many original elements are covered. - When all
nelements are covered, shrink fromito minimize the window range. - Update the result with the minimum difference between the largest and smallest values in valid windows.
- Expand
- Return the minimum deviation found.
class Solution:
def minimumDeviation(self, nums: List[int]) -> int:
n = len(nums)
arr = []
for i, num in enumerate(nums):
if num & 1:
arr.append((num, i))
arr.append((num * 2, i))
else:
while num % 2 == 0:
arr.append((num, i))
num //= 2
arr.append((num, i))
arr.sort()
res = float("inf")
seen = [0] * n
count = i = 0
for j in range(len(arr)):
seen[arr[j][1]] += 1
if seen[arr[j][1]] == 1:
count += 1
while count == n:
res = min(res, arr[j][0] - arr[i][0])
seen[arr[i][1]] -= 1
if seen[arr[i][1]] == 0:
count -= 1
i += 1
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the size of the array and is the maximum element in .
2. Min-Heap
Intuition
Instead of generating all possible values upfront, we can work incrementally. First, reduce every number to its minimum possible value (divide even numbers until they become odd). Then, we repeatedly try to increase the smallest element by doubling it (if possible), since increasing smaller values is the only way to reduce the deviation.
The min-heap lets us efficiently access the smallest current value. We track the maximum value in the heap separately. Each iteration, we pop the minimum, update our best deviation, and if that minimum can still be doubled, we push the doubled value back.
Algorithm
- Reduce each number to its minimum form by dividing even numbers by 2 until odd. Track the maximum allowed value each element can reach.
- Push each (current value, max allowed value) pair into a min-heap. Track the current maximum across all heap elements.
- While the heap contains all elements:
- Pop the minimum value and compute the current deviation (max minus min).
- If this minimum can still increase (less than its max allowed), double it and push back.
- Update the tracked maximum if needed.
- If the minimum cannot increase, stop (we cannot reduce deviation further).
- Return the minimum deviation found.
class Solution:
def minimumDeviation(self, nums: List[int]) -> int:
minHeap, heapMax = [], 0
for n in nums:
tmp = n
while n % 2 == 0:
n //= 2
minHeap.append((n, max(tmp, 2 * n)))
heapMax = max(heapMax, n)
res = float("inf")
heapq.heapify(minHeap)
while len(minHeap) == len(nums):
n, nMax = heapq.heappop(minHeap)
res = min(res, heapMax - n)
if n < nMax:
heapq.heappush(minHeap, (n * 2, nMax))
heapMax = max(heapMax, n * 2)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the size of the array and is the maximum element in .
3. Max-Heap
Intuition
We can approach this from the opposite direction: start with all numbers at their maximum possible value, then repeatedly decrease the largest element. Odd numbers are first doubled to reach their maximum. Even numbers stay as they are initially.
Using a max-heap, we always have quick access to the current largest value. We track the minimum value separately. Each iteration, we halve the maximum (if even) and update our best deviation. The process stops when the maximum is odd, since odd numbers cannot be reduced.
Algorithm
- For each number, if it is odd, double it. Push all values into a max-heap. Track the current minimum value.
- While the heap is not empty:
- Pop the maximum value and compute the current deviation (max minus min).
- Update the result if this deviation is smaller.
- If the maximum is odd, stop (cannot reduce it further).
- Otherwise, halve the maximum, push it back, and update the minimum if needed.
- Return the minimum deviation found.
class Solution:
def minimumDeviation(self, nums: List[int]) -> int:
maxHeap = []
minVal = float("inf")
for num in nums:
if num % 2 == 1:
num *= 2
heapq.heappush(maxHeap, -num)
minVal = min(minVal, num)
res = float("inf")
while maxHeap:
maxVal = -heapq.heappop(maxHeap)
res = min(res, maxVal - minVal)
if maxVal % 2 == 1:
break
nextVal = maxVal // 2
heapq.heappush(maxHeap, -nextVal)
minVal = min(minVal, nextVal)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the size of the array and is the maximum element in .
Common Pitfalls
Confusing the Operations for Odd and Even Numbers
Odd numbers can only be doubled (multiplied by 2), while even numbers can only be halved (divided by 2). A frequent mistake is applying the wrong operation or allowing both operations on any number. Remember: odd numbers increase, even numbers decrease.
Not Recognizing the Stopping Condition
When using the max-heap approach, the algorithm must stop when the maximum element is odd because odd numbers cannot be reduced further. Failing to check this condition leads to infinite loops or attempting invalid operations.
Forgetting to Track the Minimum Value Separately
When using a max-heap, you only have direct access to the maximum element. The deviation requires knowing both the max and min values. A common error is trying to find the minimum by scanning the heap, which defeats the purpose of the data structure. Track the minimum separately and update it when pushing new values.
Not Generating All Possible Values for Each Element
In the sliding window approach, each number can take multiple values. Even numbers can be halved repeatedly until odd, and odd numbers can be doubled once. Missing any possible value means the algorithm might not find the true minimum deviation.
Off-by-One Errors in the Sliding Window Count
When using the sliding window with a frequency array, ensure you correctly track when all n original elements are represented. Incrementing or decrementing the count at the wrong time can cause the window to be considered valid prematurely or remain invalid when it should be valid.
Sign in to join the discussion