[LeetCode] Minimum Moves to Equal Array Elements II 最少移動次數使數組元素相等之二


 

Given a non-empty integer array, find the minimum number of moves required to make all array elements equal, where a move is incrementing a selected element by 1 or decrementing a selected element by 1.

You may assume the array's length is at most 10,000.

Example:

Input:
[1,2,3]

Output:
2

Explanation:
Only two moves are needed (remember each move increments or decrements one element):

[1,2,3] => [2,2,3] => [2,2,2]

 

這道題是之前那道Minimum Moves to Equal Array Elements的拓展,現在我們可以每次對任意一個數字加1或者減1,讓我們用最少的次數讓數組所有值相等。一般來說這種題目是不能用暴力方法算出所有情況,因為OJ一般是不會答應的。那么這道題是否像上面一道題一樣,有巧妙的方法呢?答案是肯定的。下面這種解法實際上利用了之前一道題Best Meeting Point的思想,是不感覺很amazing,看似完全不相干的兩道題,居然有着某種內部聯系。我們首先給數組排序,那么我們最終需要變成的相等的數字就是中間的數,如果數組有奇數個,那么就是最中間的那個數字;如果是偶數個,那么就是中間兩個數的區間中的任意一個數字。而兩端的數字變成中間的一個數字需要的步數實際上就是兩端數字的距離,講到這里發現是不是就和這道題Best Meeting Point的思路是一樣了。那么我們就兩對兩對的累加它們的差值就可以了,參見代碼如下:

 

解法一:

class Solution {
public:
int minMoves2(vector<int>& nums) {
int res = 0, i = 0, j = (int)nums.size() - 1;
sort(nums.begin(), nums.end());
while (i < j) {
res
+= nums[j--] - nums[i++];
}
return res;
}
};

 

既然有了上面的分析,我們知道實際上最后相等的數字就是數組的最中間的那個數字,那么我們在給數組排序后,直接利用坐標定位到中間的數字,然后算數組中每個數組與其的差的絕對值累加即可,參見代碼如下:

 

解法二:

class Solution {
public:
int minMoves2(vector<int>& nums) {
sort(nums.begin(), nums.end());
int res = 0, mid = nums[nums.size() / 2];
for (int num : nums) {
res
+= abs(num - mid);
}
return res;
}
};

 

上面的兩種方法都給整個數組排序了,時間復雜度是O(nlgn),其實我們並不需要給所有的數字排序,我們只關系最中間的數字,那么這個stl中自帶的函數nth_element就可以完美的發揮其作用了,我們只要給出我們想要數字的位置,它就能在O(n)的時間內返回正確的數字,然后算數組中每個數組與其的差的絕對值累加即可,參見代碼如下:

 

解法三:

class Solution {
public:
int minMoves2(vector<int>& nums) {
int res = 0, n = nums.size(), mid = n / 2;
nth_element(nums.begin(), nums.begin()
+ mid, nums.end());
for (int i = 0; i < n; ++i) {
res
+= abs(nums[i] - nums[mid]);
}
return res;
}
};

 

下面這種方法是改進版的暴力破解法,它遍歷了所有的數字,讓每個數字都當作最后相等的值,然后算法出來總步數,每次和res比較,留下較小的。而這種方法叼就叼在它在O(1)的時間內完成了步數統計,那么這樣整個遍歷下來也只是O(n)的時間,不過由於還是要給數組排序,所以整體的時間復雜度是O(nlgn),這已經能保證可以通過OJ啦。那么我們來看看如何快速計算總步數,首先我們給數組排序,我們假設中間某個位置有個數字k,那么此時數組就是:nums[0], nums[1], ..., k, ..., nums[n - 1], 如果i為數字k在數組中的坐標,那么有k = nums[i],那么總步數為:

Y = k - nums[0] + k - nums[1] + ... + k - nums[i - 1] + nums[i] - k + nums[i + 1] - k + ... + nums[n - 1] - k

   = i * k - (nums[0] + nums[1] + ... + nums[i - 1]) + (nums[i] + nums[i + 1] + ... + nums[n - 1]) - (n - i) * k

   = 2 * i * k - n * k + sum - 2 * curSum

那么我們只要算出sum和curSum就可以快速得到總步數了,數組之和可以通過遍歷數組計算出來,curSum可以在遍歷的過程中累加,那么我們就可以算出總步數,然后每次更新結果res了,參見代碼如下:

 

解法四:

class Solution {
public:
int minMoves2(vector<int>& nums) {
sort(nums.begin(), nums.end());
long long sum = accumulate(nums.begin(), nums.end(), 0);
long long res = LONG_MAX, curSum = 0;
int n = nums.size();
for (int i = 0; i < n; ++i) {
long long k = nums[i];
curSum
+= k;
res
= min(res, 2 * k * (i + 1) - n * k + sum - 2 * curSum);
}
return res;
}
};

 

類似題目:

Minimum Moves to Equal Array Elements

Best Meeting Point

 

參考資料:

https://discuss.leetcode.com/topic/68764/5-line-solution-with-comment

https://discuss.leetcode.com/topic/68884/c-average-o-n-nth_element-solution

https://discuss.leetcode.com/topic/68736/java-just-like-meeting-point-problem

https://discuss.leetcode.com/topic/68900/simple-c-sort-and-find-solution-with-detailed-explanation

 

LeetCode All in One 題目講解匯總(持續更新中...)


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com