题目描述:
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
方法1:直接使用sort函数进行排序
主要思路:
(1)题目没有要求不能使用sort,所以直接使用了sort排序,然后使用反向迭代器查询第k大的数;
(2)但题目应该不是希望这样做的
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
sort(nums.begin(),nums.end());
auto it=nums.rbegin();//反向迭代器
while(--k){
++it;
}
return *it;//返回结果值
}
};
方法2:使用分治
主要思路:
(1)这里只是要找第k大的元素,也就是说,使用快速排序的方法,没必要对整个数组进行排序,只需要使用快速排序的思路,找到第 k 大的元素既可;
(2)这样,每一次选择数组中的一个元素作为分割点,然后将大于该元素的元素放到其后面,小于等于该元素的元素放大其前面,这样在遍历一遍后,就找到了该分割点在数组中的正确的位置;
(3)然后,比较该位置是否是需要的第 k 大的元素的位置,若是,则返回结果,若不是,则根据第 k 大的元素在分割点的前面,还是后面,进一步减少搜索范围,进行下一轮的搜索;
(4)在找分割点时,为了避免一些极端的情形,可以对分割点进行随机的选择,既 swap(nums[left], nums[rand() % (right - left + 1) + left]); 在LeetCode中,实测是可以提高速度;
(5)注意分割过程中的标志之间的大小关系;
class Solution {
public:
int find_index(vector<int>& nums,int left,int right){
//实现随机选择分割点,避免极端的情形
swap(nums[left], nums[rand() % (right - left + 1) + left]);
//要作为分割点的数
int pivot=nums[left];
while(left<right){
while(left<right&&nums[right]>pivot){//保证右侧是大于分割点的数
--right;
}
nums[left]=nums[right];//将不满足要求的数值放入左侧
while(left<right&&nums[left]<=pivot){//保证左侧是小于分割点的数
++left;
}
nums[right]=nums[left];//将不满足要求的点放入右侧
}
nums[left]=pivot;//找到分割点的位置
return left;
}
int findKthLargest(vector<int>& nums, int k) {
int target=nums.size()-k;//k在升序序列中对应的索引值
//左右边界
int left=0;
int right=nums.size()-1;
while(left<=right){
int pos=find_index(nums,left,right);//找出一个分割点的位置
if(pos==target){//判断当前分隔点是否是需要的位置
return nums[pos];
}
//根据分割点和要找的位置的关系,更新新的搜索范围
if(pos<target){
left=pos+1;
}
else if(pos>target){
right=pos-1;
}
}
return nums[left];
}
};