面试题目:如何在10亿个整数中找出前1000个最大的数。
方法1:冒泡排序
冒泡排序原理:
通过两层for循环,外层第一次循环找到数组中最大的元素放在倒数第一个位置,第二次循环找到第二大的元素放到倒数第二个位置。。。。循环N次就可以找到TopN。
缺点:
冒泡排序内层循环需要大量交换元素,复杂度介于O(n)和O(n*2)之间。
方法2:分治法
快排原理:
选一个基准元素,每次排序将这个基准元素搁置在正确的位置上,左边的元素比他小,右边的元素比他大,从而将数组分成左右两个部分,分而治之。
TopN问题
选择一个基准元素并通过快速排序将基准元素搁置在正确的位置,如果左边的元素个数小于1000,那么继续从基准右边排序,如果左边元素大于1000,那么从基准元素左边排序,知道基准的位置正好在1000,结束。
缺点:
第一次排序复杂度是O(n),第二次排序是O(n/2),第三次排序是O(n/4)....
方法3:小顶堆
小顶堆的性质如下:
每个父节点的值都小于左右孩子节点,然后依次从文件中读取10亿个整数,如果元素比顶堆小,则跳过不进行任何操作,如果比顶堆大,则把顶堆元素替换掉,并重新构建小顶堆。当10亿个整数遍历完成后,堆内元素就是Topn的结果。
时间复杂度O(nlogn)
java代码实现
维护一个大小为N 的最小堆或者最大堆来动态更新TopN元素,java中提供了PriorityQueue
类可以实现堆结构。
public class TopN {
public static void main(String[] args) {
int n=5;
PriorityQueue<Integer> pq=new PriorityQueue<>(n);
int[] data={10, 7, 2, 5, 8, 4, 9, 3, 6, 1};
for (int num: data) {
pq.offer(num);
if(pq.size()>n){
pq.poll();//维护堆的大小为N
}
}
//输出前N个最大元素
while(!pq.isEmpty()){
System.out.println(pq.poll());
}
}
}