1675.Minimize Deviation in Array === ###### tags: `Hard`,`Array`,`Greedy`,`Heap` [1675. Minimize Deviation in Array](https://leetcode.com/problems/minimize-deviation-in-array/) ### 題目描述 You are given an array `nums` of `n` positive integers. You can perform two types of operations on any element of the array any number of times: * If the element is **even**, **divide** it by `2`. * For example, if the array is `[1,2,3,4]`, then you can do this operation on the last element, and the array will be `[1,2,3,2]`. * If the element is **odd**, **multiply** it by `2`. * For example, if the array is `[1,2,3,4]`, then you can do this operation on the first element, and the array will be `[2,2,3,4]`. The **deviation** of the array is the **maximum difference** between any two elements in the array. Return *the **minimum deviation** the array can have after performing some number of operations.* ### 範例 **Example 1:** ``` Input: nums = [1,2,3,4] Output: 1 Explanation: You can transform the array to [1,2,3,2], then to [2,2,3,2], then the deviation will be 3 - 2 = 1. ``` **Example 2:** ``` Input: nums = [4,1,5,20,3] Output: 3 Explanation: You can transform the array after two operations to [4,2,5,5,3], then the deviation will be 5 - 2 = 3. ``` **Example 3:** ``` Input: nums = [2,10,8] Output: 3 ``` **Constraints**: * `n` == `nums.length` * 2 <= `n` <= 5 * 10^4^ * 1 <= `nums[i]` <= 10^9^ ### 解答 #### C++ ##### 思路: 1. 奇數只能*2, 偶數只能/2, 找經過操作後最小的deviation 2. 因為奇數做*2後, 必定成為偶數且接下來只能/2, 故可以把當前奇數都*2後, 以當前數列全為偶數做思考 3. 根據題目我們其實也只關心最大跟最小值的差值, 所以可以紀錄最小值後, 開始把最大值提出來/2, 看不可以縮小差距, 可以的話持續迭代, 直到最大值是奇數不能再/2結束 ```cpp= class Solution { public: int minimumDeviation(vector<int>& nums) { int min_v = INT_MAX, res = INT_MAX; priority_queue<int> q; for(int i = 0; i < nums.size(); i++) { nums[i] = nums[i]&1 ? nums[i]*2 : nums[i]; min_v = min(min_v, nums[i]); q.push(nums[i]); } res = q.top() - min_v; while(q.top() % 2 == 0) { int v = q.top()/2; q.pop(); q.push(v); min_v = min(min_v, v); res = min(res, q.top()-min_v); } return res; } }; ``` Time: $O(nlog(n))$ 最多做`n`次, 每次push一個值進priority queue Extra Space: $O(n)$ > [name=XD] [time= Feb 24, 2023] ##### 方法1 跟天宇一樣,但考慮到數字是$2^d$的倍數的話,那時間複雜度其實是$O(nd\log n)$ Runtime 602ms (Beats 38.66%) ```cpp= class Solution { public: int minimumDeviation(vector<int>& nums) { int lower = INT_MAX; priority_queue<int> pq; for (int i = 0; i < nums.size(); i++) { nums[i] <<= nums[i] & 1; lower = min(lower, nums[i]); pq.push(nums[i]); } int ans = pq.top() - lower; while (pq.top() % 2 == 0) { int value = pq.top() / 2; pq.pop(); pq.push(value); lower = min(lower, value); ans = min(ans, pq.top() - lower); } return ans; } }; ``` > [name=Yen-Chi Chen][time=Sat, Feb 25, 2023] ##### 方法2 利用set來減少相同的數字重複檢查,複雜度一樣是$O(nd\log n)$,但執行時間能減少接近一半 Runtime 333ms (Beats 89.8%) ```cpp= class Solution { public: int minimumDeviation(vector<int>& nums) { int lower = INT_MAX; set<int> s; for (int i = 0; i < nums.size(); i++) { nums[i] <<= nums[i] & 1; lower = min(lower, nums[i]); s.insert(nums[i]); } int ans = *s.rbegin() - lower; while (*s.rbegin() % 2 == 0) { int value = *s.rbegin(); s.erase(value); value = value / 2; s.insert(value); lower = min(lower, value); ans = min(ans, *s.rbegin() - lower); } return ans; } }; ``` > [name=Yen-Chi Chen][time=Sat, Feb 25, 2023] ##### 方法3 - 思路 1. 從前面的方法中會發現,將每個數字一直除以2之後,最終會停在最大的奇數(max_odd)上。 2. 那試想一下,最小的deviation發生時,這個數列是否會包含這個max_odd呢?答案是肯定的。證明也很簡單,在不失一般性的情況下,我們先假設max_odd還未出現,仍是$2\times$max_odd,我們可以將數列中$\geq 2 \times$max_odd的所有數字都除以2,這樣整個數列的range就會縮小了,而且此時數列中就會包含max_odd。 3. 接下來就是想辦法讓數列中的數字盡量維持在max_odd附近就好。 4. 再加一個小技巧,除以2這個運算需要考慮到一個數字是不是偶數,但如果我們把所有的偶數都先除以2一次,這個在max_odd附近的數列,無論奇偶數都只需要考慮乘2就好。 - 作法 1. 找出每個數字除2到變為奇數後,找出最大的奇數出來。 2. 對原始的數列中的數字,奇數不動,偶數的一律都先除以2一次,之後再持續除到不大於max_odd。然後將這個數列排序。 3. 如圖,嘗試把從前面開始的一段數列乘以2,找出紅線範圍最小的情況。 ![](https://i.imgur.com/MrqPhhR.png) Time: $O(nd + n\log n)$ Space: $O(1)$ ![](https://i.imgur.com/KJuF0rn.png) ```cpp= class Solution { public: int minimumDeviation(vector<int>& nums) { int max_odd = 1; for (auto num : nums) { while (num % 2 == 0) num >>= 1; max_odd = max(max_odd, num); } for (auto& num : nums) { if (num % 2 == 0) { num >>= 1; while (num > max_odd) num >>= 1; } } sort(nums.begin(), nums.end()); int ans = max_odd - nums[0], n = nums.size(); for (int i = 0; i < n - 1; i++) { ans = min(ans, max(max_odd, nums[i] * 2) - min(nums[i+1], nums[0] * 2)); } return ans; } }; ``` > [name=Yen-Chi Chen][time=Sat, Feb 25, 2023] #### Javascript ```javascript= function minimumDeviation(nums) { const maxHeap = new MaxHeap(); let min = Infinity; for (const num of nums) { if (num % 2 === 1) { maxHeap.push(num * 2); min = Math.min(min, num * 2); } else { maxHeap.push(num); min = Math.min(min, num); } } let result = maxHeap.top() - min; while (maxHeap.top() % 2 === 0) { const max = maxHeap.pop(); min = Math.min(min, max / 2); maxHeap.push(max / 2); result = Math.min(result, maxHeap.top() - min); } return result; } // Priority Queue class MaxHeap { constructor() { this.queue = []; } push(val) { this.queue.push(val); this.bubbleUp(); return this; } pop() { const val = this.queue[0]; const last = this.queue.pop(); if (this.queue.length > 0) { this.queue[0] = last; this.bubbleDown(); } return val; } top() { return this.queue[0]; } bubbleUp() { let index = this.queue.length - 1; let parentIndex = ~~((index - 1) / 2); while (this.queue[index] > this.queue[parentIndex]) { [this.queue[index], this.queue[parentIndex]] = [this.queue[parentIndex], this.queue[index]]; index = parentIndex; parentIndex = ~~((index - 1) / 2); } return this; } bubbleDown() { let index = 0; let leftIndex = index * 2 + 1; let rightIndex = index * 2 + 2; let maxIndex = index; while (leftIndex < this.queue.length) { if (this.queue[leftIndex] > this.queue[maxIndex]) { maxIndex = leftIndex; } if (rightIndex < this.queue.length && this.queue[rightIndex] > this.queue[maxIndex]) { maxIndex = rightIndex; } if (maxIndex === index) break; [this.queue[index], this.queue[maxIndex]] = [this.queue[maxIndex], this.queue[index]]; index = maxIndex; leftIndex = index * 2 + 1; rightIndex = index * 2 + 2; } return this; } } ``` > 參考樓上兩位大神的方法1,先寫個max heap練習一下,結果錯了好幾次... > [name=Marsgoat][time=Mar 2, 2023] ### Reference https://leetcode.com/problems/minimize-deviation-in-array/solutions/3224070/javascript-8-lines-greedy-no-pq-time-o-nlogn-space-o-1-100/ 分享一下看到一些很神的做法 [回到題目列表](https://hackmd.io/@Marsgoat/leetcode_every_day)