前言:此类问题就是经典TopK问题
快速排序的详细解析可移至博主另外一篇博文
几种常见排序
下面直接给出题解~
常规快速排序
public int findKth(int[] a, int n, int K) {
return quickSort(a,0,n-1,K);
}
//快速排序
public int quickSort(int[] a,int left,int right,int k){
if(left < right){
int point = partition(a,left,right);
if (point == k-1)
return a[k-1];
else if (point > k-1)
quickSort(a,left,point-1,k);
else
quickSort(a,point+1,right,k);
}
return a[k-1];
}
//分割
public int partition(int[] a,int left,int right){
//基准值
int base = a[left];
while(left < right){
while (left < right && a[right] <= base)
right--;
swap(a,left,right);
while(left < right && a[left] >= base)
left++;
swap(a,left,right);
}
return left;
}
private void swap(int[] a, int left, int right) {
int tmp = a[left];
a[left] = a[right];
a[right] = tmp;
}
基于快排思想
这种方法也是快速排序,跟上面常规的快排有异曲同工之妙。
import java.util.*;
public class Finder {
public int findKth(int[] a, int n, int K) {
int left = 0, right = n-1;
//转换成目标值在数组中的索引
int target = n-K;
while(true){
int index = partition(a,left,right);
if(index == target)
return a[index];
//a[index] 为数组中第index大的值
else if(index < target)
left = index+1;
else
right = index-1;
}
}
public int partition(int[] a, int left, int right){
//以该值为切片
int pivot = a[left];
int j = left;
for(int i = left+1; i <= right; i++){
//如果切片右边的数小于它,则把小的值放在切片的右邻
if(a[i] < pivot){
j++;
swap(a,j,i);
}
}
//此时上面循环算出来后区间[left+1,j]<pivot && [j+1,right]>pivot
//下面我们交换left和j的值
swap(a,left,j); //此时[left,j-1]<pivot && a[j]==pivot && [j+1,right]>pivot
return j; //此时因为pivot右边的值都比它大,左边的值都比它小,所以返回它的下标回到主程序判断是否为目标值的下标
}
public void swap(int[] a, int j, int i){
int temp = a[j];
a[j] = a[i];
a[i] = temp;
}
}
优化
基准值取随机数
/*添加随机寻找基准值*/
int random_index = new Random().nextInt(right-left+1)+left;
swap(a,left,random_index); 
京公网安备 11010502036488号