题目链接: https://leetcode.com/problems/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]
思路: 一开始以为是找平均数然后求差值, wrong了一次之后感觉这题似曾相识, 记不得具体是哪题了, 好像是要找中位数的差值. 好吧, 暂时猜想为中位数, 那么还要证明其正确性.
考虑数组长度为奇数时, 如果最终的值小于中位数或者大于中位数, 则必然导致中位数那个数的移动. 举个例子, 数组长度为2n+1, 则中位数两边各有n个数, 如果左右两边移动到中位数的位置, 设左边所有数和中位数的差值和为x, 右边所有数和中位数的差值和为y, 则所有需要移动的次数为x+y. 但是如果最终的数不选择中位数, 比如选择median-1, 则左边的数移动到median-1需要至少(x-n)次移动(这里是至少, 有可能多于, 比如某个数大于median-1, 则需要向左移动), 而中位数右边的数移动到(median-1)则需要(y+n), 同时中位数还需移动一次. 这样总的移动次数就变成了 >= ((x-n) + (y+n) + 1). 显然比移动到中位数多移动了1. 同理选择一个大于中位数的值也一样.
再来考虑长度为偶数的情况, 这种情况下中位数为两个, 那么此时应该选择这两个的哪一个呢, 或者选其平均数? 答案是都可以! 我们可以先这样考虑, 让左边的中位数左边的数都移动到那个中位数的位置, 右边的的中位数右边的所有数都移动到右边中位数的位置, 这样所有的数就分成了两派, 隔海相望. 此时是你去找我还是我去找你, 或者两者都往中间走在任意位置相遇, 所消耗的代价都是一样的, 所以对于偶数长度的数组中位数同样成立! 证明完毕.
代码如下:
class Solution {
public:
int minMoves2(vector<int>& nums) {
int len = nums.size(), ans = 0;
nth_element(nums.begin(), nums.begin()+len/2, nums.end());
for(auto val: nums) ans += abs(val - nums[len/2]);
return ans;
}
};