topk
1、topk问题
- 采用小根堆或者大根堆
求最大K个采用小根堆,而求最小K个采用大根堆。
求最大K个的步奏:
- 根据数据前K个建立K个节点的小根堆。
- 在后面的N-K的数据的扫描中,
- 如果数据大于小根堆的根节点,则根节点的值覆为该数据,并调节节点至小根堆。
- 如果数据小于或等于小根堆的根节点,小根堆无变化。
求最小K个跟这求最大K个类似。时间复杂度O(nlogK)(n:数据的长度),特别适用于大数据的求Top K。
package test1;
/**
* 求前面的最大K个 解决方案:小根堆 (数据量比较大(特别是大到内存不可以容纳)时,偏向于采用堆)
*
*
*/
public class topk {
public static void main(String[] args) {
int a[] = { 4, 3, 5, 1, 2, 8, 9, 10 };
int result[] = new topk().getTopKByHeap(a, 3);
for (int temp : result) {
System.out.println(temp);
}
}
/**
* 创建k个节点的小根堆
*
* @param a
* @param k
* @return
*/
int[] createHeap(int a[], int k) {
int[] result = new int[k];
for (int i = 0; i < k; i++) {
result[i] = a[i];
}
for (int i = 1; i < k; i++) {
int child = i;
int parent = (i - 1) / 2;
int temp = a[i];
while (parent >= 0 && child != 0 && result[parent] > temp) {
result[child] = result[parent];
child = parent;
parent = (parent - 1) / 2;
}
result[child] = temp;
}
return result;
}
void insert(int a[], int value) {
a[0] = value;
int parent = 0;
while (parent < a.length) {
int lchild = 2 * parent + 1;
int rchild = 2 * parent + 2;
int minIndex = parent;
if (lchild < a.length && a[parent] > a[lchild]) {
minIndex = lchild;
}
if (rchild < a.length && a[minIndex] > a[rchild]) {
minIndex = rchild;
}
if (minIndex == parent) {
break;
} else {
int temp = a[parent];
a[parent] = a[minIndex];
a[minIndex] = temp;
parent = minIndex;
}
}
}
int[] getTopKByHeap(int input[], int k) {
int heap[] = this.createHeap(input, k);
for (int i = k; i < input.length; i++) {
if (input[i] > heap[0]) {
this.insert(heap, input[i]);
}
}
return heap;
}
}
2、topk 大根堆 求最小的K个数
package test1;
import java.util.*;
/**
* 无序数组中最小的k个数
*
* @author 过路的守望
*
*/
public class Main_2 {
public static void main(String[] args) {
Scanner s =new Scanner(System.in);
int n=s.nextInt();
int k=s.nextInt();
int[] A=new int[n];
for(int i=0;i<n;i++) {
A[i]=s.nextInt();
}
int[] res=findKthNumbers(A,n,k);
for(int i=0;i<k;i++) {
System.out.println(res[i]);
}
// System.out.println(Arrays.toString(res));
}
public static int[] findKthNumbers(int[] A, int n, int k) {
n = A.length;
/*
* p为数组中第k大的数
*/
int p = getKthNumberByHeapSort(A, k);
int[] result = new int[k];
int pos = 0;
// System.out.println(p);
for (int i = 0; i < n; i++) {
if (A[i] <= p) {
result[pos++] = A[i];
}
}
return result;
}
/**
* 取数组前k个元素作为初始堆元素
*
* @param A
* @param k
* @return
*/
public static int getKthNumberByHeapSort(int[] A, int k) {
int[] heap = new int[k];
heap = Arrays.copyOf(A, k);
/*
* 建堆
*/
buildHeap(heap, k);
/*
* 将数组后n-k个元素与堆顶元素比较,若比其小,则赋值给堆顶元素后执行下滤操作. 时间复杂度
*/
for (int i = k; i < A.length; i++) {
if (A[i] < heap[0]) {
heap[0] = A[i];
perColate(heap, 0, k);
}
}
return heap[0];
}
/**
* 建堆过程
*
* @param data
* @param len
*/
public static void buildHeap(int[] data, int len) {
for (int i = (len - 1) / 2; i >= 0; i--) {
perColate(data, i, len);
}
}
/**
* 下滤操作 最大堆
*
* @param data
* @param i
* @param length
*/
public static void perColate(int[] data, int i, int length) {
int len = length;
int leftChild = getLeftChild(i);
int temp = data[i];
while (leftChild < len) {
if (leftChild < (len - 1) && data[leftChild] < data[leftChild + 1]) {
leftChild++;
}
if (temp < data[leftChild]) {
data[i] = data[leftChild];
i = leftChild;
leftChild = getLeftChild(i);
} else {
break;
}
}
data[i] = temp;
}
/**
* 得到左儿子下标
*
* @param parent
* @return
*/
public static int getLeftChild(int parent) {
return 2 * parent + 1;
}
}