| 每日一题做题记录,参考官方和三叶的题解 |
题目要求
理解
找中位数,属于TopK问题。
思路:数学(中位数)
- 很容易想到,都变成中间的数就好了。
- 证明过程懒得写,请看三叶姐姐
- 所以把数组排个序,找中位数,计算每个数和中位数的距离之和即可。
Java
class Solution {
public int minMoves2(int[] nums) {
Arrays.sort(nums);
int n = nums.length, mid = nums[(n - 1) / 2], res = 0;
for(int i : nums)
res += Math.abs(mid - i);
return res;
}
}
- 时间复杂度:
- 空间复杂度:
C++
class Solution {
public:
int minMoves2(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size(), mid = nums[(n - 1) / 2], res = 0;
for(int i : nums)
res += abs(mid - i);
return res;
}
};
- 时间复杂度:
- 空间复杂度:
思路:快速选择
- 和刚才的思路一样,不过排序算法换成快排,基于快排找数组中第小的元素。
Java
【注意快排移动指针时的加一和减一】
class Solution {
Random ran = new Random(); // 用于随机拆分
int[] nums;
public int minMoves2(int[] nums) {
this.nums = nums;
int n = nums.length, mid = quickSelect(0, n - 1, n / 2), res = 0;
for(int i : nums)
res += Math.abs(i - mid);
return res;
}
// 快速选择[f,l]范围内第target大的数
public int quickSelect(int first, int last, int target) {
int q = randomPartition(first, last);
if(q == target)
return nums[q];
else
return q < target ? quickSelect(q + 1, last, target) : quickSelect(first, q - 1, target); //注意左右区间
}
public int randomPartition(int first, int last) {
int i = ran.nextInt(last - first + 1) + first;
swap(i, last);
return partition(first, last);
}
public int partition(int first, int last) {
int x = nums[last], i = first - 1;
for(int j = first; j < last; ++j) {
if(nums[j] <= x) {
++i;
swap(i, j);
}
}
swap(i + 1, last);
return i + 1;
}
public void swap(int a, int b) {
int t = nums[a];
nums[a] = nums[b];
nums[b] = t;
}
}
- 时间复杂度:
- 空间复杂度:
C++
C++有一个类似快排思路的内置函数
class Solution {
public:
int minMoves2(vector<int>& nums) {
int mid = nums.size() / 2;
nth_element(nums.begin(), nums.begin() + mid, nums.end());
int res = 0;
for(int i : nums)
res += abs(i - nums[mid]);
return res;
}
};
- 时间复杂度:
- 空间复杂度:
STL nth_element()
- 学习参考链接
nth_element(first, n, last, comp)- 找数组/容器在到范围内第大的数,也可以改为降序找第小的数;
- 为自定义排序规则,可选。
总结
本来以为是个简单的数学推导,找中位数的简单题。可以引申到TopK问题,使用快排解决,get新函数nth_element。
【莫名开心的一天】
| 欢迎指正与讨论! |