zoukankan      html  css  js  c++  java
  • TopK问题:什么是TopK问题?用堆和快排这两种方式来实现TopK

      目录

    一、什么是Top K问题

    二、Top K的实际应用场景

    三、Top K的代码实现及其效率对比

      1.用堆来实现Top K

      2.用快排来实现Top K

      3.用堆或用快排来实现 TopK 的效率对比

      正文

    一、什么是Top K问题?

      给一个无序的数组,长度为N,  请输出最小 (或最大)的K个数。

    二、Top K的实际应用场景

      排行榜:用户数量有几百万, 但是只需要前100名的用户成绩。 要显示出来, 且这个排行榜是实时变化的。

    三、Top K的代码实现

      需求:给一个无序的数组,长度为N, 请输出最大的5个数。 

      1. 用堆来实现Top K——PriorityQueue(小顶堆)

      (1)步骤梳理:

        ①创建一个结点个数为 k 的小顶堆;

        ②当数据量 < k 时,将数据直接放到这个小顶堆中,此时堆的顶结点是最小值;

        ③当数据量 >= k时,每产生一个新数据都与堆的顶结点进行比较: 

          如果新数据 > 顶结点数据,则将顶结点删除,将新数据放到堆中,此时堆会进行排序,且维护了堆的总结点数为k;

                            如果新数据<顶结点数据,则不动。

      (2)中心思想:使堆的总结点数维持在 k 个。

      (3)代码实现:

     1     @Test
     2     public void getTopKByHeapInsertTopKElement() {
     3         int arrayLength = 10000000 + 10;
     4         int topK = 5;
     5 
     6         // 准备一个长度为arrayLength的无序数组:
     7         int[] array = A03TopKByQuickSortAndNewArray.getDisorderlyArray(arrayLength);
     8 
     9         // 准备一个总结点数为topK的小顶堆:
    10         PriorityQueue<Integer> heap = new PriorityQueue<>(topK);
    11 
    12         long start = System.currentTimeMillis();
    13         
    14         // 始终维持一个总结点个数为k的堆:
    15         insertButmaintainTheHeapAtTopK(heap, array, topK);
    16         
    17         //获得最大topK:
    18         printHeap(heap);
    19         
    20         long end = System.currentTimeMillis();
    21         System.out.println("获得最大top5总耗时: " + (end - start));
    22     }
    23     
    24     /**
    25      * 用小顶堆来获取topK:当数据量超过topK后,新产生的数据直接和heap的顶结点进行比较。
    26      */
    27     private static void insertButmaintainTheHeapAtTopK(PriorityQueue<Integer> heap, int[] array, int topK) {
    28         for (int i = 0; i < array.length; i++) {
    29             if (i < topK) {
    30                 heap.add(array[i]);
    31             } else {// 怎么维持堆的总结点个数,下面的代码是关键:
    32                 if (null != heap.peek() && array[i] > heap.peek()) {
    33                     heap.poll();
    34                     heap.add(array[i]);
    35                 }
    36             }
    37         }
    38     }
    39     
    40     /**
    41      * 获取最大TopK
    42      * @param heap
    43      */
    44     static void printHeap(PriorityQueue<Integer> heap) {
    45         Iterator<Integer> iterator = heap.iterator();
    46         while (iterator.hasNext()) {
    47             System.out.println(iterator.next());
    48         }
    49     }

      

      2. 用快排来实现Top K

      (1)步骤梳理:

        ①通过快排,先将无序数组array进行排序;

        ②取出最小Top 5,并放到topArray中;【关键】

        ③超过arrayLength个数据后,又产生了insertNumber个新数据:直接和topArray数组比较,要放也是放到topArray中了;【关键】

      (2)时间复杂度:

        ①排序的时间复杂度:O(N*logN);

        ②取出top k的时间复杂度:O(1),就是遍历数组。

      (3)代码实现:

      1     @Test
      2     public void testGetTopKByQuickSortToNewArray() {
      3         int topK = 5;
      4         int arrayLength = 10000000;
      5         
      6         //准备一个无序数组
      7         int[] array = getDisorderlyArray(arrayLength);
      8         
      9         long start = System.currentTimeMillis();
     10         
     11         //1.通过快排,先将无序数组array进行排序
     12         quickSort(array, 0, array.length-1);
     13         
     14         //2.取出最小Top 5,并放到topArray中:
     15         int[] topKArray = insertToTopArrayFromDisorderlyArray(array, topK);
     16         
     17         //3.超过arrayLength个数据后,又产生了insertNumber个新数据:直接和topArray[topKArray.length-1]比较,要放也是放到topArray中了
     18         insertToTopKArray(topKArray, 10, 100, topKArray.length-1);//生成10个100以内的随机数作为新数据,和topKArray[topKArray.length-1]
     19         
     20         long end = System.currentTimeMillis();
     21         System.out.println("获得最大top5总耗时: " + (end - start));
     22     }
     23     
     24     /**
     25      * 产生新的数据后,再和topKArray数组进行比较,看新数据时候需要插入到topKArray中,若需要插入,则堆topKArray进行重新快排。
     26      * 
     27      * @param topKArray topK数组
     28      * @param insertNumber 新产生的数据的个数
     29      * @param randomIntRange 在什么范围内产生新数据,如生成10以内的随机数。
     30      * @param topK 在topKArray中,确定要替换的元素的下标。获得最小topK,则topK是从小到大排序的topKArray的最后一个元素。
     31      */
     32     private static void insertToTopKArray(int[] topKArray, int insertNumber, int randomIntRange, int topK) {
     33         Random random = new Random();
     34         int randomInt;
     35         for(int i = 0; i < insertNumber; i++) {
     36             randomInt = random.nextInt(100);
     37             if(randomInt < topKArray[topK]) {//新数据如果小于topArray[topK],则直接用该数去替换topArray,然后再将topArray进行重新排序。
     38                 topKArray[topK] = randomInt;
     39                 quickSort(topKArray, 0, topKArray.length-1);
     40             }
     41         }
     42     }
     43     
     44     /**
     45      * 从有序数组中取出需要的TopK,放到TopK数组中。
     46      * 
     47      * @param sourceArray 有序数组
     48      * @param topK 需要获取到Top K
     49      * @return TopK数组
     50      */
     51     private static int[] insertToTopArrayFromDisorderlyArray(int[] sourceArray, int topK) {
     52         int[] topArray = new int[topK];
     53         for(int i = 0; i < 5; i++) {
     54             topArray[i] = sourceArray[i];
     55         }
     56         return topArray;
     57     }
     58     
     59     /**
     60      * 快排
     61      * @param target
     62      * @param left
     63      * @param right
     64      */
     65     static void quickSort(int[] target, int left, int right) {
     66         if (left >= right) {
     67             return;
     68         }
     69         int pivot = target[left];// 基准点
     70         int temp;
     71         int i = left;
     72         int j = right;
     73         while (i < j) {
     74             while (target[j] >= pivot && i < j) {
     75                 j--;
     76             }
     77             while (target[i] <= pivot && i < j) {
     78                 i++;
     79             }
     80             if (i < j) {
     81                 temp = target[i];
     82                 target[i] = target[j];
     83                 target[j] = temp;
     84             }
     85         }
     86         // left和right相遇了:
     87         // ①将相遇点的元素和pivot做交换:
     88         target[left] = target[j];
     89         target[j] = pivot;
     90         // ②基准点两边的元素的分别再做排序:
     91         quickSort(target, left, j - 1);
     92         quickSort(target, j + 1, right);
     93     }
     94     
     95     /**
     96      * 准备一个无序数组
     97      * 
     98      * @param arrayLength
     99      * @return int[]
    100      */
    101     static int[] getDisorderlyArray(int arrayLength) {
    102         int[] disorderlyArray = new int[arrayLength];
    103         Random random = new Random();
    104         for (int i = 0; i < arrayLength; i++) {
    105             disorderlyArray[i] = random.nextInt(arrayLength);
    106         }
    107         return disorderlyArray;
    108     }
    109     
    110     /**
    111      * 遍历数组
    112      */
    113     static void showArray(int[] target) {
    114         for (Integer element : target) {
    115             System.out.println(element);
    116         }
    117     }

      3. 用堆来实现TopK 和 用快排来实现TopK 的效率对比:

                      “小顶堆”    |    “快排”

        数据量为100万+10时:    11毫秒    |    124毫秒

        数据量为1000万+10时:    28毫秒    |    1438毫秒

    作者:赖皮梅
    声明:
    1.原创博文,欢迎转载、引用;转载、引用请注明作者并附上原文链接,否则保留追究法律责任的权利。
    2.本博文中引用他人的博文内容时均已注明出处,如有侵权,请联系作者删除。
    3.博文内容如有错误、不妥之处,欢迎留言指正,还请不吝赐教 =^_^=
  • 相关阅读:
    ie6,ie7,ie8 css bug汇总以及兼容解决方法
    关于ie6下的双倍浮动
    团队合作,如何避免js冲突
    实例探索Class文件
    MFC获得本机的IP
    MessageBox英文显示OK/Cancel(适用于中英文界面)
    基于OpenCV的程序脱离动态链接库运行方法 (此方法也可用于将opencv源码编译成一个链接库)
    将摄像头原始RGB数据流编码成H.264文件
    在线程中创建非模态对话框——解决对话框创建一闪就消失问题
    .NET预处理器指令
  • 原文地址:https://www.cnblogs.com/laipimei/p/11715064.html
Copyright © 2011-2022 走看看