目录

一、什么是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)代码实现:

     @Test
public void getTopKByHeapInsertTopKElement() {
int arrayLength = 10000000 + 10;
int topK = 5; // 准备一个长度为arrayLength的无序数组:
int[] array = A03TopKByQuickSortAndNewArray.getDisorderlyArray(arrayLength); // 准备一个总结点数为topK的小顶堆:
PriorityQueue<Integer> heap = new PriorityQueue<>(topK); long start = System.currentTimeMillis(); // 始终维持一个总结点个数为k的堆:
insertButmaintainTheHeapAtTopK(heap, array, topK); //获得最大topK:
printHeap(heap); long end = System.currentTimeMillis();
System.out.println("获得最大top5总耗时: " + (end - start));
} /**
* 用小顶堆来获取topK:当数据量超过topK后,新产生的数据直接和heap的顶结点进行比较。
*/
private static void insertButmaintainTheHeapAtTopK(PriorityQueue<Integer> heap, int[] array, int topK) {
for (int i = 0; i < array.length; i++) {
if (i < topK) {
heap.add(array[i]);
} else {// 怎么维持堆的总结点个数,下面的代码是关键:
if (null != heap.peek() && array[i] > heap.peek()) {
heap.poll();
heap.add(array[i]);
}
}
}
} /**
* 获取最大TopK
* @param heap
*/
static void printHeap(PriorityQueue<Integer> heap) {
Iterator<Integer> iterator = heap.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}

  

  2. 用快排来实现Top K

  (1)步骤梳理:

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

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

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

  (2)时间复杂度:

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

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

  (3)代码实现:

     @Test
public void testGetTopKByQuickSortToNewArray() {
int topK = 5;
int arrayLength = 10000000; //准备一个无序数组
int[] array = getDisorderlyArray(arrayLength); long start = System.currentTimeMillis(); //1.通过快排,先将无序数组array进行排序
quickSort(array, 0, array.length-1); //2.取出最小Top 5,并放到topArray中:
int[] topKArray = insertToTopArrayFromDisorderlyArray(array, topK); //3.超过arrayLength个数据后,又产生了insertNumber个新数据:直接和topArray[topKArray.length-1]比较,要放也是放到topArray中了
insertToTopKArray(topKArray, 10, 100, topKArray.length-1);//生成10个100以内的随机数作为新数据,和topKArray[topKArray.length-1] long end = System.currentTimeMillis();
System.out.println("获得最大top5总耗时: " + (end - start));
} /**
* 产生新的数据后,再和topKArray数组进行比较,看新数据时候需要插入到topKArray中,若需要插入,则堆topKArray进行重新快排。
*
* @param topKArray topK数组
* @param insertNumber 新产生的数据的个数
* @param randomIntRange 在什么范围内产生新数据,如生成10以内的随机数。
* @param topK 在topKArray中,确定要替换的元素的下标。获得最小topK,则topK是从小到大排序的topKArray的最后一个元素。
*/
private static void insertToTopKArray(int[] topKArray, int insertNumber, int randomIntRange, int topK) {
Random random = new Random();
int randomInt;
for(int i = 0; i < insertNumber; i++) {
randomInt = random.nextInt(100);
if(randomInt < topKArray[topK]) {//新数据如果小于topArray[topK],则直接用该数去替换topArray,然后再将topArray进行重新排序。
topKArray[topK] = randomInt;
quickSort(topKArray, 0, topKArray.length-1);
}
}
} /**
* 从有序数组中取出需要的TopK,放到TopK数组中。
*
* @param sourceArray 有序数组
* @param topK 需要获取到Top K
* @return TopK数组
*/
private static int[] insertToTopArrayFromDisorderlyArray(int[] sourceArray, int topK) {
int[] topArray = new int[topK];
for(int i = 0; i < 5; i++) {
topArray[i] = sourceArray[i];
}
return topArray;
} /**
* 快排
* @param target
* @param left
* @param right
*/
static void quickSort(int[] target, int left, int right) {
if (left >= right) {
return;
}
int pivot = target[left];// 基准点
int temp;
int i = left;
int j = right;
while (i < j) {
while (target[j] >= pivot && i < j) {
j--;
}
while (target[i] <= pivot && i < j) {
i++;
}
if (i < j) {
temp = target[i];
target[i] = target[j];
target[j] = temp;
}
}
// left和right相遇了:
// ①将相遇点的元素和pivot做交换:
target[left] = target[j];
target[j] = pivot;
// ②基准点两边的元素的分别再做排序:
quickSort(target, left, j - 1);
quickSort(target, j + 1, right);
} /**
* 准备一个无序数组
*
* @param arrayLength
* @return int[]
*/
static int[] getDisorderlyArray(int arrayLength) {
int[] disorderlyArray = new int[arrayLength];
Random random = new Random();
for (int i = 0; i < arrayLength; i++) {
disorderlyArray[i] = random.nextInt(arrayLength);
}
return disorderlyArray;
} /**
* 遍历数组
*/
static void showArray(int[] target) {
for (Integer element : target) {
System.out.println(element);
}
}

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

                  “小顶堆”    |    “快排”

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

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

利用堆来处理Top K问题的更多相关文章

  1. 优先队列PriorityQueue实现 大小根堆 解决top k 问题

    转载:https://www.cnblogs.com/lifegoesonitself/p/3391741.html PriorityQueue是从JDK1.5开始提供的新的数据结构接口,它是一种基于 ...

  2. Top K问题的两种解决思路

    Top K问题在数据分析中非常普遍的一个问题(在面试中也经常被问到),比如: 从20亿个数字的文本中,找出最大的前100个. 解决Top K问题有两种思路, 最直观:小顶堆(大顶堆 -> 最小1 ...

  3. 优先队列实现 大小根堆 解决top k 问题

      摘于:http://my.oschina.net/leejun2005/blog/135085 目录:[ - ] 1.认识 PriorityQueue 2.应用:求 Top K 大/小 的元素 3 ...

  4. 堆与堆排序、Top k 问题

     堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法.学习堆排序前,先讲解下什么是数据结构中的二叉堆. 二叉堆的定义 二叉堆是完全二叉树或者是近似完全二叉树. 二叉堆满 ...

  5. 堆实战(动态数据流求top k大元素,动态数据流求中位数)

    动态数据集合中求top k大元素 第1大,第2大 ...第k大 k是这群体里最小的 所以要建立个小顶堆 只需要维护一个大小为k的小顶堆 即可 当来的元素(newCome)> 堆顶元素(small ...

  6. 经典面试问题: Top K 之 ---- 海量数据找出现次数最多或,不重复的。

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  7. [LeetCode] Top K Frequent Words 前K个高频词

    Given a non-empty list of words, return the k most frequent elements. Your answer should be sorted b ...

  8. 程序员编程艺术:第三章续、Top K算法问题的实现

    程序员编程艺术:第三章续.Top K算法问题的实现 作者:July,zhouzhenren,yansha.     致谢:微软100题实现组,狂想曲创作组.     时间:2011年05月08日    ...

  9. 海量数据处理 - 10亿个数中找出最大的10000个数(top K问题)

    前两天面试3面学长问我的这个问题(想说TEG的3个面试学长都是好和蔼,希望能完成最后一面,各方面原因造成我无比想去鹅场的心已经按捺不住了),这个问题还是建立最小堆比较好一些. 先拿10000个数建堆, ...

随机推荐

  1. 防抖与节流 & 若每个请求必须发送,如何平滑地获取最后一个接口返回的数据

    博客地址:https://ainyi.com/79 日常浏览网页中,在进行窗口的 resize.scroll 或者重复点击某按钮发送请求,此时事件处理函数或者接口调用的频率若无限制,则会加重浏览器的负 ...

  2. 通过CSS3实现圆形头像显示

    很久没更新博客了,因为比较菜,也没什么能在上面分享的.作为新手,马上要毕业找工作了,最近又在重新学习web的一些知识,刚刚学到CSS3,跟大家分享一些比较有趣的知识.今天分享的是利用CSS实现圆形头像 ...

  3. 死磕 java线程系列之线程池深入解析——未来任务执行流程

    (手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:线程池源码部分如无特殊说明均指ThreadPoolExecutor类. 简介 前面我们一起学习了线程池中普 ...

  4. Security整合spring boot

    Security整合spring boot 1.基础概念 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spri ...

  5. Java虚拟机之垃圾回收

    简述 Java与那些较传统的语言比如C++有个很大不同就是垃圾回收策略了.前者通常是虚拟机自动帮我们做了,而后者就需要我们手动来完成. Java虚拟机帮我们完成了垃圾回收,是不是意味着我们就不用完全去 ...

  6. [考试反思]0719NOIP模拟测试6 + 0722NOIP模拟测试7

    连续爆炸,颇为愉快. 第6次:Rank #4 第7次:Rank #9 对于第6次考试,个人比较满意,因为T1只是差了一个卡常. 因为在考试前两天刚讲了矩阵,满脑子都是矩阵,还想到了循环矩阵优化. 整个 ...

  7. CSPS模拟 79

    T1 建一颗新树,倍增 T2 WARNING:竞赛图如果有环,则最小环一定为三元环 (发现这个结论的这把都稳了) 然后三元环计数,发现部分分都是为了审出题意但是不会正解的人设的.. 由于对于任意一种方 ...

  8. [LLL邀请赛]参观路线(图论+dfs)

    emmmm....学校的oj被查水表了,扒不到原题面,所以.... 但是我还是扒到了题面... 题目大意:给定一个完全图,删掉其中一些边,然后求其字典序最小的遍历顺序 有点像去年day2T1啊.... ...

  9. P2860()

    题目描述: 为了从F(1≤F≤5000)个草场中的一个走到另一个,贝茜和她的同伴们有时不得不路过一些她们讨厌的可怕的树.奶牛们已经厌倦了被迫走某一条路,所以她们想建一些新路,使每一对草场之间都会至少有 ...

  10. CSS尺寸样式属性

    尺寸样式属性介绍 属性 值 含义 height auto:自动,浏览器会自动计算高度length:使用px定义高度%:基于包含它的块级对象的百分比高度 设置元素高度 width 同上 设置元素的宽度 ...