
你必须知道的数学知识:给你n个数,每个数字都可以不断进行+1和-1操作,让这n个数字通过若干次+1或-1操作使其全部相同。最少的操作次数方案为,找到这n个数的中位数,然后所有其它数字通过+1和-1操作向中位数看齐。
快速选择算法找中位数
很多同学会直接排序,然后找到中位数,然后依次遍历进行+1和-1操作。但是直接排序就需要直接用快速排序算法。时间复杂度还不如用小根堆来的快呢。毕竟小根堆也是专门处理这类的问题的好手。而快速排序是将每一个数的位置找到,我们这里的需求只是找第k个数。
| 解题思路:时间复杂度O(
n
n
n),空间复杂度O(
l
o
g
2
n
log_2n
log2n) |
|---|
- 中位数就相当于找第k大数(k是n个数的中间位置),那么很多小伙伴会直接想到堆排序(小根堆)。但是堆排序它更适合将前K大的数都找出来,而只是找一个数字,快速选择更好。因为堆排序对k-1,k-2等数都会进行操作。有兴趣的可以参考215题
- 215题中,其中一个方法就是使用快速选择算法找第k个数,因此这里不再赘述快速选择的逻辑
- 我们的核心就是,先通过快速选择算法,找到中位数,最后其它数字全部通过+1和-1操作,让其和中位数相等。

class Solution {
public int minMoves2(int[] nums) {
int n = nums.length;
int ans = 0;
int k = n % 2 == 0 ? (n+2) / 2 : (n + 1) / 2;
int h = quickSort(nums,0,n-1,k);
for(int num : nums){
ans += Math.abs(h - num);
}
return ans;
}
int quickSort(int[] a, int left,int right,int k){
if(left >= right) return a[left];
int x = a[left + right >> 1];
int leftIndex = left - 1, rightIndex = right + 1;
while(leftIndex < rightIndex){
do leftIndex++;while(a[leftIndex] < x);
do rightIndex--;while(a[rightIndex] > x);
if(leftIndex < rightIndex){
swap(a,leftIndex,rightIndex);
}
}
if(k <= (rightIndex - left + 1))
return quickSort(a,left,rightIndex,k);
else
return quickSort(a,rightIndex+1,right,k-(rightIndex-left+1));
}
void swap(int[] a,int i,int j){
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}