#Topk问题
问题要求
- 输入很大一组数据N,找到该数据中最大的k个数。
- 例如:
- 输入一组数据:1,6,7,2,3,4,8,5,9,0
- 输入k :5
- 输出:5 6 7 9 8
- 例如:
思路分析
- 可以使用堆结构解决该问题:
- (1) 首先选取这一堆数字的前k个存到一个k大小的数组
- (2)把这长度为k的数组构建成一个小根堆,
- (3)将剩下的N-k个数字,循环遍历依次和小根堆的根节点进行比较,如果当前数字比根节点的值大,则将当前数字赋值给根节点,再将修改过的堆转换为小根堆。重复直到N-k个数字全部遍历完成。
- (4)最终得到的数组便是长度为k的,最大的k个数。
代码实现
public class TopK {
private int[] arr;
public TopK(int k){
this.arr = new int[k];
}
/**
* 从上向下,将根节点变成最小的数
* @param root
* @param len
*/
public void AdjustDown(int root, int len){
int parent = root;
int child = 2*parent + 1;
while(child < len){
if(child+1 < len&&arr[child] > arr[child+1]){
child++;
}
if(arr[child] < arr[parent]){
int tmp = arr[child];
arr[child] = arr[parent];
arr[parent] = tmp;
parent = child;
child = 2*parent + 1;
}else{
break;
}
}
}
/**
* 先将数组中前K个元素找到,并将这前K个元素建成小根堆
* @param arr1 表示输入的一组数据
* @param k 表示k个元素
* @return
*/
public int[] createminHeap(int[] arr1,int k){
for(int i = 0;i < k;i++){//将数组arr1中的前k个元素复制到arr里面 。
arr[i] = arr1[i];
}
for(int i = (arr.length-1-1)/2;i >= 0;i--){//通过递归将arr转换为小根堆
AdjustDown(i,arr.length);
}
return arr;
}
/**
* @param arr1 表示输入的一组数据
* @param k 表示k个元素
* @return
*
* 将数组arr1中剩下的arr1.length-k个数与arr[0]进行比较,如果比arr[0]大,则交换两个数,
* 再将arr调整为小根堆,依次遍历,直到将arr1.length-k个数全部与arr[0]比较一遍。
*
*/
public int[] insert(int[] arr1 ,int k){
for(int i = k;i < arr1.length;i++){//
if(arr1[i] > arr[0]){
arr[0] = arr1[i];
createminHeap(arr,k);
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = {1,6,7,2,3,4,8,5,9,0};
int k = 5;
TopK topK = new TopK(k);
topK.createminHeap(arr,k);
int[] arr1 = topK.insert(arr,k);
for(int i = 0;i < arr1.length;i++){
System.out.print(arr1[i]+" ");
}
}
}
总结
如果数据太多,而一个数组存不下时,则可以将数据分为n小份,然后每一份又是topk问题了,然后对于结果就是k*n的数据量,再将其合起来求topk。