版权声明:本文为博主原创文章,转载请注明原文链接 https://blog.csdn.net/qq_36922927/article/details/82527611
一,什么是topK问题?
一组数据中,需要找出前k大(小)的数据
二,思路分析:以前k小为例
思路1:先排序再输出
如果是完全有序,那么输出所求数据很简答了(需要排序,将数据完全排序)
思路2:基于快排,分片,不完全排序
使用快排思想,不断分片,左侧是比基准小的元素,右侧是比基准大的元素,
令 index=基准元素下标
a, index<k 说明第k小元素在index右侧,在index右侧寻找新的基数,直到基数的下标index==k
b,index==k 说明第k小元素就是基准元素
c,index>k 说明第k小元素在index左侧,在index左侧寻找新的基数,直到基数的下标index==k
三,实现 java代码实现:
public static int topK(int [] array,int k) throws Exception {
if(null==array||array.length==0){
throw new Exception("数组不能为空!");
}
if(k>array.length||k<=0){
throw new Exception("k 范围不合法");
}
//开始处理快排:如何找基准数?基准数的大小越靠中间越好
int middleIndex=getMiddle(array,0,array.length-1);
while(middleIndex!=k){
//去右侧找新的基数位置
if(middleIndex<k){
middleIndex=getMiddle(array,middleIndex+1,array.length-1);
}else{
//去左侧找新的基数位置
middleIndex=getMiddle(array,0,middleIndex-1 );
}
}
return array[middleIndex];
}
getMiddle方法实现:
//找到array中下标在from,to范围的基数的下标(同时是在通过快排思想将元素分割,移动)
public static int getMiddle(int []array,int from,int to){
int middle=array[from];//暂存基准值
while(from<to){
//从右往左扫描
while(from<to && array[to]>=middle){
to--;
}
array[from]=array[to];//暂存这个比较神奇的to所指元素
// (array[to] 该元素可能是
// 1,from==to条件下产生,from==to,那这个赋值没啥影响
// 2,也可能是array[to]<middle条件下产生,此赋值将比middle小的元素移动到middle所在位置
//从左往右扫描
while(from<to && array[from]<middle){
from++;
}
array[to]=array[from];//
}
// from==to,退出while循环,此时已经保证比基准值middle小
的元素在from左侧 ,不小于基准值middle的元素在from右侧
array[from]=middle;//将基准值(放回数组)放到from这个位置
return from;
}
附上产生随机数组代码
/**
*@author wangwei
*@created 11:17 2018/9/8
*@classname RandomUtil
*@classdescription 随机数生成工具类
*
*/
public class RandomUtil {
/**
*
* @param size 随机数组容量
* @param max 随机数最大值
* @return 随机数组
*/
public static int [] buildArray(int size, int max){
int []array=new int[size];
for(int i=0;i<size;i++){
array[i]= (int) (Math.random()*max)%max;
}
return array;
}
public static void main(String[] args) {
System.out.println( RandomUtil.buildArray(20,100).toString());
}
}
测试代码:
public static void main(String[] args) throws Exception {
final int k=4;
int [] array= RandomUtil.buildArray(10,20);
showArray("before topK",array);
System.out.println("第"+(k+1)+"小值:"+topK(array,k));
showArray("after topK",array);
}
public static void showArray(String info,int []array){
System.out.println(info);
for(int item:array){
System.out.println(item);
}
}
测试结果:
这是一次测试结果:注意的是,基准之前的元素并不保证是否有序,只是保证其比基准元素小,基准之后的元素同理
before topK
14
4
3
3
6
6
3
13
11
6
第5小值:6
after topK
3
4
3
3
6
6
6
13
11
14