注意:此题是借助中位数而不是平均数
因为每次操作可以使元素减1或加1,则所有元素排序之后必定是往中间靠拢,当所以元素的值都和中卫数相等时,操作完成。
证明如下:
1)设为2n+1个数,中位数是m,排序之后如下
*******a m b ********,其中a<=m<=b,m左右两边分别都有N个数
设m左边的数变成m的代价是x,m右边的数变成m代价是y,则选择中位数的代价是x+y,
现在选择a,则m左边的数变成a的代价是x-(m-a)*n,m右边的数变成a的代价是y+(m-a)*n,相加为x+y,但m也需要变成a,则总的代价是x+y+m-a,
若a<m,则选择a时代价大于选择m;若a==m,则两者代价相等。
现在选择b,则m左边的数变成a的代价是x+(b-m)*n,m右边的数变成a的代价是y-(b-m)*n,相加为x+y,但m也需要变成b,则总的代价是x+y+b-m,
若m<b,则选择b的代价大于m;若b==m,则两者代价相等。
2)设为2n个数,排序之后如下
*******a b ********,其中a<=b,a左边,b右边分别都有N-1个数
如果选择把所有的数变成a,设a左边的数变成a的代价为x,b右边的数变成b的代价为y,则总的代价为x+y+b-a,因为b也要变成a.
如果选择把所有数变成b,则a左边的数变成b的代价为x+(b-a)*(n-1),b右边的代价为y-(b-a)*(n-1),则总的代价仍然为x+y+b-a,因为a也要变成b。
即选取中位数合理,且中位数直接去nums[len/2]即可,然后计算每个数与中位数的差值。
还可以排序之后left指向0,right指向len-1,直接计算nums[right]-nums[left]的累加和
因为nums[right]-nums[len/2]+nums[len/2]-nums[left]=nums[right]-nums[left]。
a<=x<=b,则a,b都往x靠拢的操作数为x-a+b-x=b-a
代码如下:
class Solution {
public:
int minMoves2(vector<int>& nums) {
//每次移动只能让一个元素加1或者减1,应该是中位数,而不是平均数
int len=nums.size();
sort(nums.begin(),nums.end());
int res=0;
int left=0,right=len-1;
while(left<right)
res+=nums[right--]-nums[left++];
return res;
}
};