1.问题
求数组中第k个最大元素:给定整数数组 nums
和整数 k
,请返回数组中第 k
个最大的元素。请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
2.方法分析
eg:示例2排序后:122334556,k=4,这里的第k(4)大元素说的是从最大往小数到的第四个元素,即输出4【即求排序后的倒数第k个元素】,而不是说输出从大往小的第四个不同元素3。
1)排序(nlogN)
将nums数组排序,取arr.length-k这个索引对应的元素即可。
提问:若现在要求时间复杂度<nlogn呢?
2)使用优先级队列(nlogK)
取大用小,先建立k个元素的最小堆,扫描一遍集合,就保存了前k个最大元素,此时取出堆顶元素即可。(堆顶元素就是要找的第k大的元素)
3)使用快排的分区函数(O(n))
要求一个集合的第k大元素,恰好就是集合排序后的第n-k个索引位置的值,分区函数不就是在返回排序后的最后索引吗?每划分区间一次元素就确定了该元素位置且返回了该元素对应索引位置,若这个索引恰好等于n-k位置索引,那么问题直接解决。
3.代码实现
基于快排分区函数的代码实现
public class Num251_FindKLargest {
private Random random=new Random();
public int findKthLargest(int[] nums, int k) {
//利用快排分区函数,在分区时找到分区后元素索引与nums.length-k索引相同时的值,这个值就是要找的值
//返回最大元素
//nums.length-k:倒数第k个位置;第k个最大元素就是排序后倒数第nums.length-k的元素
if(k>nums.length){
throw new NoSuchElementException("illegal k,error");
}
return quickSortSelect(nums,0,nums.length-1,nums.length-k);
}
//选择是否进行排序,==情况不用排序了直接返回值即可,此题主要是求值
private int quickSortSelect(int[] nums, int l, int r, int index) {
//如果index和返回的分界点的索引相同,就不用继续快排了
int p=partition(nums,l,r);//返回分界点索引
if(p==index){
return nums[p];
}
//排序好的分区点索引不等于倒数第k个索引index
//分区点下标<index,递归右区间,>index递归左区间
return p<index?quickSortSelect(nums,p+1,r,index):quickSortSelect(nums,l,p-1,index);
}
//nums[l...r]进行排序
private int partition(int[] nums, int l, int r) {
int randomIndex=random.nextInt(r-l+1)+l;
swap(nums,l,randomIndex);
int v=nums[l];
//nums[l+1...j]<v
//nums[j+1...i)>=v
int j=l;
//i时当前正在遍历元素
for (int i = l+1; i <= r ; i++) {
if(nums[i]<v){
swap(nums,i,j+1);
j++;
}//else只需要i++,条件里已经写了
}
//循环完毕一次分区就分好了,交换l与j索引位置元素(j位置是<v的最后一个元素)
swap(nums,l,j);
return j;
}
private void swap(int[] nums, int i, int j) {
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}