首先,按上图把数组分成很多个子数组,每个子数组有5个元素,分别排列,可以得到很多个按顺序排列的子数组。咱们找到这些数组中位数的中位数即为M,这个M是可以保证有相当一部分元素在其左边和右边,即对最坏选择点加了限制。
之后就是常见的分治做法,交给子问题去做即可。
下面为选择第k大的元素的代码:
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
// 若少于5个,直接排序结束
if(nums.size() < 5)
{
sort(nums.begin(), nums.end());
return nums[min(nums.size()-1, nums.size()-k)];
}
// 如果所有元素相同,直接返回
int same = 1;
for(int i=1;i<nums.size();i++)
{
if(nums[i-1] != nums[i])
{
same = 0;
break;
}
}
if(same == 1)return nums[0];
// 排序,得到中位数数组
vector<int> mid;
for(int i=0;i<nums.size()/5;i++)
{
sort(nums.begin()+i*5, nums.begin()+i*5+5);
mid.push_back(nums[i*5+2]);
}
// 得到中位数的中位数,及在原数组中坐标
int median = findKthLargest(mid, mid.size()/2);
// 分段做
vector<int> l, r;
for(int i=0;i<nums.size();i++)
{
if(nums[i] <= median)l.push_back(nums[i]);
else r.push_back(nums[i]);
}
if(r.size() >= k)return findKthLargest(r, k);
else return findKthLargest(l, k-r.size());
}
};
如果要找前k个数的话,最好先找到第k个数再扫两遍,不然分治直接返回结果会比较复杂。