1. 问题描述
给定一个数字数组,求其中前K大的数字
2. 解题思路
https://blog.csdn.net/u013132035/article/details/82936032
- 快排—将所有数据排序,返回其中前K大的数字
- 冒泡—局部排序;冒K次泡,就把最大的K个数都冒上来
- 堆排序 — 维护一个容量为k的堆保存topk的数组,然后对每个数字比较一遍。思路也很简单,复杂度是O(nlogn)
方法一:排序
排序是最容易想到的方法,将n个数排序之后,取出最大的k个,即为所得。
伪代码:
sort(arr, 1, n);
return arr[1, k];
时间复杂度:O(n*lg(n))
分析:明明只需要TopK,却将全局都排序了,这也是这个方法复杂度非常高的原因。那能不能不全局排序,而只局部排序呢?这就引出了第二个优化方法。
方法二:冒泡–局部排序
不再全局排序,只对最大的k个排序。
冒泡是一个很常见的排序方法,每冒一个泡,找出最大值,冒k个泡,就得到TopK。
伪代码:
for(i=1 to k){
bubble_find_max(arr,i);
}
return arr[1, k];
时间复杂度:O(n*k)
分析:冒泡,将全局排序优化为了局部排序,非TopK的元素是不需要排序的,节省了计算资源。不少朋友会想到,需求是TopK,是不是这最大的k个元素也不需要排序呢?这就引出了第三个优化方法。
方法三: 堆
思路:只找到TopK,不排序TopK。
- 先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。
- 接着,从第k+1个元素开始扫描,和堆顶(堆中最小的元素)比较,如果被扫描的元素大于堆顶,则替换堆顶的元素,并调整堆,以保证堆内的k个元素,总是当前最大的k个元素。
- 直到,扫描完所有n-k个元素,最终堆中的k个元素,就是求的TopK。
伪代码:
heap[k] = make_heap(arr[1, k]);
for(i=k+1 to n){
adjust_heap(heep[k],arr[i]);
}
return heap[k];
Java代码
public class TopK {
/**
* 创建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 <