题目描述
从10亿个随机整数中,找出前1000个最大数,要求以最小的时间复杂度及空间复杂度实现该需求。
解题思路
这道题的解决思路是将无序整数排列为有序序列,升序或降序,从中取出前1000个最大数。适用的排序算法包括:
- 插入排序、选择排序、冒泡排序、快速排序等,时间复杂度:
- 堆排序、归并排序,时间复杂度:
本题需要实现的是找出前1000个最大数,空间上可以只做1000条最大目标数组,其他数据与目标数组进行比较,如果比较值小于目标数组最小值,则抛弃。否则将最小值替换为比较值,并使用排序算法完成数据排序。本题采用算法为构建一个大小为1000的小顶堆排序(读者可自行查阅堆排序相关资料)。空间复杂度:
。排序算法及时间复杂度参考见下图:
排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 | 复杂性 |
直接插入排序 | O(n2) | O(n2) | O(n) | O(1) | 稳定 | 简单 |
希尔排序 | O(nlog2n) | O(n2) | O(n1.3) | O(1) | 不稳定 | 较复杂 |
直接选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 | 简单 |
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 不稳定 | 较复杂 |
冒泡排序 | O(n2) | O(n2) | O(n) | O(1) | 稳定 | 简单 |
快速排序 | O(nlog2n) | O(n2) | O(nlog2n) | O(nlog2n) | 不稳定 | 较复杂 |
归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 稳定 | 较复杂 |
基数排序 | O(d(n+r)) | O(d(n+r)) | O(d(n+r)) | O(n+r) | 稳定 | 较复杂 |
堆排序的特性
- 堆是一颗完整二叉树,除了最后一层节点不是满的,其余每一层从左到右都是满的。
- 堆中的每个节点的整数值,都小于或等于这个节点的子节点的整数。
- 对使用数组实现二叉树,满足以下条件公式:
- 节点的左子节点的index为:2*index + 1
- 节点的右子节点的index为:2*index + 2
- 节点的父节点的index为:(index-1)/2
堆-完全二叉树
代码实现
说明:直接通过内存生成10亿条数据,容易产生堆内存溢出,可分批加载(如每次10000条)并完成小顶堆排序,并输出1000条数据。本例为方便测试验证,仅测试100条数据,查找最大的10条。
public class HeapSortTest { //10亿数字. public static int LEN = 100; //TOP 1000 public static int TOP = 10 ; //10亿数字数组 public static int arr[] = new int[LEN] ; //TOP1000的小顶堆数组 public static int res[] = new int[TOP] ; public static void main(String[] args) { for (int i = 0; i < LEN; i++) { arr[i] = new Random().nextInt(1000); } //构建初始堆 for (int i = 0; i < TOP; i++) { res[i] = arr[i]; } //构建小顶堆 long start = System.currentTimeMillis() ; buildMinHeap(res); for (int i = TOP; i res[0]) { res[0] = arr[i]; minHeap(res, 0); } } System.out.println(LEN + "个数,求Top" + TOP + ",耗时" + (System.currentTimeMillis() - start) + "毫秒"); System.out.println(Arrays.toString(res)); } /** * 自底层向上构建一个小顶堆 */ public static int[] buildMinHeap(int[] arr) { for(int i= arr.length/2 -1; i>=0; i--) { minHeap(arr, i) ; } return arr ; } /** * 根据插入的值,生成小顶堆 */ private static void minHeap(int[] arr, int current) { int left = current*2 + 1, right = current*2 + 2, min = current ; if(left < arr.length && arr[left] < arr[min]) { min = left; } else if(right < arr.length && arr[right] < arr[min]) { min = right ; } if(min != current) { int tmp = arr[min] ; arr[min] = arr[current] ; arr[current] = tmp ; minHeap(arr, min); } }}