堆排序应用之topK问题

Java基础

浏览数:235

2019-10-8

题目:求海量数据(正整数)按逆序排列的前k个数(topK),因为数据量太大,不能全部存储在内存中,只能一个一个地从磁盘或者网络上读取数据,请设计一个高效的算法来解决这个问题。 第一行用户输入K,代表要求得topK  随后的N(不限制)行,每一行是一个整数代表用户输入的数据 直到用户输入-1代表输入终止 请输出topK,空格分割。

思路:先开辟一个K大小的数组arr,然后读取K个数据存储到数组arr,读到K+1的时候,如果arr[K+1]小于arr中最小的值,那么就丢掉不管,如果arr[K+1]大于arr中最小的值,那么就把arr[K+1]和数组中最小的值进行交换,然后再读取K+2个数。这样就能解决这个问题。但是这个算法复杂度为K+(N-K)*K,K可以忽略不计,所以时间复杂度为O(KN)。那这个代码很容易就写出来。假如题目要求用到NlgK的时间复杂度,那么这里就需要使用堆这种数据结构来解决问题,而且还是小顶堆。具体思想还是和数组一样。

代码:

 1 import java.util.Arrays;
 2 import java.util.Scanner;
 3 
 4 public class TopK {
 5 
 6     static int[] heap;
 7     static int index = 0;
 8     static int k;
 9     
10     public static void main(String[] args) {
11         Scanner scanner = new Scanner(System.in);
12         k = scanner.nextInt();
13         heap = new int[k];
14         int x = scanner.nextInt();
15         while(x!=-1){
16             deal(x);  // 处理x
17             x = scanner.nextInt();
18         }
19         System.out.println(Arrays.toString(heap));
20     }
21 
22     /**
23      * 如果数据量小于等于k,直接加入堆中
24      * 等于k的时候,进行堆化
25      * @param x
26      */
27     private static void deal(int x) {
28         if (index<k) {
29             heap[index++] = x;
30             if (index==k) {
31                 // 堆化
32                 makeMinHeap(heap);
33                 System.out.println(Arrays.toString(heap));
34             }
35         }else if (heap[0]<x) {
36             heap[0] = x;
37             MinHeapFixDown(heap, 0, k);
38             System.out.println(Arrays.toString(heap));
39         }else {
40             System.out.println(Arrays.toString(heap));
41         }
42     }
43     static void makeMinHeap(int[] A){
44         int n = A.length;
45         for(int i = n/2-1;i>=0;i--){
46             MinHeapFixDown(A,i,n);
47         }
48     }
49     
50     private static void MinHeapFixDown(int[] A, int i, int n) {
51         //  找到左右孩子
52         int left = 2 * i + 1;
53         int right = 2 * i + 2 ;
54         // 左孩子已经越界,i就是叶子节点
55         if (left>=n) {
56             return ;
57         }
58         // min 指向了左右孩子中较小的那个
59         int min = left;
60         if (right>=n) {
61             min = left;
62         }else {
63             if (A[right]<A[left]) {
64                 min = right;
65             }
66         }
67         // 如果A[i]比两个孩子都要小,不用调整
68         if (A[i]<=A[min]) {
69             return ;
70         }
71         // 否则,找到两个孩子中较小的,和i交换
72         int temp = A[i];
73         A[i] = A[min];
74         A[min] = temp;
75         // 小孩子那个位置的值发生了变化,i变更为小孩子那个位置,递归调整
76         MinHeapFixDown(A, min, n);
77     }
78 
79 }

结果:

  

总结:partition和堆都能解决顺序统计量问题,堆更适用于海量数据流,适用于不能全部存储在内存中的数据,相当于实时数据流,而partition适用于能够一次加载到内存的数组。

作者:|旧市拾荒|