`
IXHONG
  • 浏览: 451603 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java最小堆解决TopK问题

阅读更多

www.toutiao.im

其实我们与大数据并不遥远,比如要从海量数据中按大小或频率挑出top k,假定机器是多核的内存有限的,我们采用多线程分块处理数据,最后合并处理。那么,处理每一块数据的top k(i)可以采用哪些算法呢?

 

TopK问题是指从大量数据(源数据)中获取最大(或最小)的K个数据。

TopK问题是个很常见的问题:例如学校要从全校学生中找到成绩最高的500名学生,再例如某搜索引擎要统计每天的100条搜索次数最多的关键词。

 

对于这个问题,解决方法有很多:

 

方法一:对源数据中所有数据进行排序,取出前K个数据,就是TopK。

但是当数据量很大时,只需要k个最大的数,整体排序很耗时,效率不高。

方法二:维护一个K长度的数组a[],先读取源数据中的前K个放入数组,对该数组进行升序排序,再依次读取源数据第K个以后的数据,和数组中最小的元素(a[0])比较,如果小于a[0]直接pass,大于的话,就丢弃最小的元素a[0],利用二分法找到其位置,然后该位置前的数组元素整体向前移位,直到源数据读取结束。

这比方法一效率会有很大的提高,但是当K的值较大的时候,长度为K的数据整体移位,也是非常耗时的。

 

对于这种问题,效率比较高的解决方法是使用最小堆

 

最小堆(小根堆)是一种数据结构,它首先是一颗完全二叉树,并且,它所有父节点的值小于或等于两个子节点的值

最小堆的存储结构(物理结构)实际上是一个数组。如下图:

 

 

堆有几个重要操作:

BuildHeap:将普通数组转换成堆,转换完成后,数组就符合堆的特性:所有父节点的值小于或等于两个子节点的值。

Heapify(int i):当元素i的左右子树都是小根堆时,通过Heapify让i元素下降到适当的位置,以符合堆的性质。

 

回到上面的取TopK问题上,用最小堆的解决方法就是:先取源数据中的K个元素放到一个长度为K的数组中去,再把数组转换成最小堆。再依次取源数据中的K个之后的数据和堆的根节点(数组的第一个元素)比较,根据最小堆的性质,根节点一定是堆中最小的元素,如果小于它,则直接pass,大于的话,就替换掉跟元素,并对根元素进行Heapify,直到源数据遍历结束。

 

 

最小堆的实现:

[java] view plain copy
  1. public class MinHeap  
  2. {  
  3.     // 堆的存储结构 - 数组  
  4.     private int[] data;  
  5.       
  6.     // 将一个数组传入构造方法,并转换成一个小根堆  
  7.     public MinHeap(int[] data)  
  8.     {  
  9.         this.data = data;  
  10.         buildHeap();  
  11.     }  
  12.       
  13.     // 将数组转换成最小堆  
  14.     private void buildHeap()  
  15.     {  
  16.         // 完全二叉树只有数组下标小于或等于 (data.length) / 2 - 1 的元素有孩子结点,遍历这些结点。  
  17.         // *比如上面的图中,数组有10个元素, (data.length) / 2 - 1的值为4,a[4]有孩子结点,但a[5]没有*  
  18.         for (int i = (data.length) / 2 - 1; i >= 0; i--)   
  19.         {  
  20.             // 对有孩子结点的元素heapify  
  21.             heapify(i);  
  22.         }  
  23.     }  
  24.       
  25.     private void heapify(int i)  
  26.     {  
  27.         // 获取左右结点的数组下标  
  28.         int l = left(i);    
  29.         int r = right(i);  
  30.           
  31.         // 这是一个临时变量,表示 跟结点、左结点、右结点中最小的值的结点的下标  
  32.         int smallest = i;  
  33.           
  34.         // 存在左结点,且左结点的值小于根结点的值  
  35.         if (l < data.length && data[l] < data[i])    
  36.             smallest = l;    
  37.           
  38.         // 存在右结点,且右结点的值小于以上比较的较小值  
  39.         if (r < data.length && data[r] < data[smallest])    
  40.             smallest = r;    
  41.           
  42.         // 左右结点的值都大于根节点,直接return,不做任何操作  
  43.         if (i == smallest)    
  44.             return;    
  45.           
  46.         // 交换根节点和左右结点中最小的那个值,把根节点的值替换下去  
  47.         swap(i, smallest);  
  48.           
  49.         // 由于替换后左右子树会被影响,所以要对受影响的子树再进行heapify  
  50.         heapify(smallest);  
  51.     }  
  52.       
  53.     // 获取右结点的数组下标  
  54.     private int right(int i)  
  55.     {    
  56.         return (i + 1) << 1;    
  57.     }     
  58.   
  59.     // 获取左结点的数组下标  
  60.     private int left(int i)   
  61.     {    
  62.         return ((i + 1) << 1) - 1;    
  63.     }  
  64.       
  65.     // 交换元素位置  
  66.     private void swap(int i, int j)   
  67.     {    
  68.         int tmp = data[i];    
  69.         data[i] = data[j];    
  70.         data[j] = tmp;    
  71.     }  
  72.       
  73.     // 获取对中的最小的元素,根元素  
  74.     public int getRoot()  
  75.     {  
  76.             return data[0];  
  77.     }  
  78.   
  79.     // 替换根元素,并重新heapify  
  80.     public void setRoot(int root)  
  81.     {  
  82.         data[0] = root;  
  83.         heapify(0);  
  84.     }  
  85. }  

 

利用最小堆获取TopK:

[java] view plain copy
  1. public class TopK  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.         // 源数据  
  6.         int[] data = {56,275,12,6,45,478,41,1236,456,12,546,45};  
  7.           
  8. // 获取Top5  
  9.         int[] top5 = topK(data, 5);  
  10.           
  11.         for(int i=0;i<5;i++)  
  12.         {  
  13.             System.out.println(top5[i]);  
  14.         }  
  15.     }  
  16.       
  17.     // 从data数组中获取最大的k个数  
  18.     private static int[] topK(int[] data,int k)  
  19.     {  
  20.         // 先取K个元素放入一个数组topk中  
  21.         int[] topk = new int[k];   
  22.         for(int i = 0;i< k;i++)  
  23.         {  
  24.             topk[i] = data[i];  
  25.         }  
  26.           
  27.         // 转换成最小堆  
  28.         MinHeap heap = new MinHeap(topk);  
  29.           
  30.         // 从k开始,遍历data  
  31.         for(int i= k;i<data.length;i++)  
  32.         {  
  33.             int root = heap.getRoot();  
  34.               
  35.             // 当数据大于堆中最小的数(根节点)时,替换堆中的根节点,再转换成堆  
  36.             if(data[i] > root)  
  37.             {  
  38.                 heap.setRoot(data[i]);  
  39.             }  
  40.         }  
  41.           
  42.         return topk;  
  43. }  
  44. }  

介绍完了最小堆,顾名思义,相应的还有最大堆。

www.toutiao.im

 

系统可用内存10M,从一个2G的文本文件里统计出现次数排名前10的单词,用MapReduce再合适不过,分而治之。

 

0
1
分享到:
评论

相关推荐

    topK 问题的5种解决方案

    ### TopK 问题的五种解决方案 在计算机科学与数据处理领域中,TopK 问题是一种常见的需求场景,其核心任务是从一个数组或列表中找到最大的 K 个元素。这类问题广泛应用于各种场合,比如搜索引擎返回最相关的 K 条...

    Java实现TopK问题的方法

    Java实现TopK问题的方法是指在大量数据中找到TopK个最大或最小的元素, 这是一个常见的算法问题。下面将从两种方法来实现Java实现TopK问题:基于快排的TopK实现和堆排序实现TopK。 基于快排的TopK实现: 快排是最...

    最大(小)堆Java实现

    在Java中,最大堆通常用于优先队列的实现,比如在解决Top K问题、排序等问题时非常有效。 在Java中,我们可以通过自定义类来实现最大堆。下面将详细解释最大堆的实现过程,包括数据结构设计、插入、删除和筛选建立...

    Java实现堆并调整

    此外,堆也是解决Top-K问题和近似中位数查找的有效工具。通过理解和熟练运用堆的操作,可以显著提升算法的效率。 总的来说,`Heapest.java`和`HeapOperator.java`的代码可能包含了堆数据结构的创建、插入、删除以及...

    TOPK算法的Hash实现

    标题中的“TOPK算法的Hash实现”指的是使用哈希数据结构来解决找出数据集中最大或最小的K个元素的问题。这种算法通常用于大数据处理和实时分析中,因为哈希表可以提供快速的查找和更新操作。 TOPK算法的核心是通过...

    java实现TOP查询(java作业)

    这个“java实现TOP查询”的作业来自东北大学软件学院的java期末项目,旨在让学生掌握分布式TOPK算法的基本实现。这里我们将深入探讨相关知识点。 首先,让我们了解什么是TOP查询。在数据库或数据处理中,TOP查询...

    java一亿数字取前100个(3秒钟获取)

    7. **TopK算法**:设计一种TopK算法,用于动态维护当前已知的最小K个元素。在本例中,K=100。每读取一个数字,只需与TopK队列中的最大元素比较,如果小于该元素则替换之,始终保持队列大小不超过100。 通过以上策略...

    一个简单的top-k实现

    因此,它是解决Top-K问题的理想选择。 以下是一个基本的Java Top-K算法实现思路: 1. 初始化一个大小为K的优先队列,设置队列的比较器以降序排列元素。 2. 遍历输入数据集,对于每个元素,如果队列的大小小于K,则...

    Java~三种重写compare方法的PriorityQueue、TopK问题的解决思想附练习题(查找最小的K对数字与最后一块石头重量)

    本文将介绍如何通过重写`Comparator`接口的`compare`方法来实现这个需求,并结合`TopK`问题探讨解决策略。 ### 重写`compare`方法 #### 方法一:静态内部类 创建一个静态内部类,继承自`Comparator`接口,并重写`...

    java算法大全(含源码包)

    8. **堆与优先队列**:如最大堆、最小堆,常用于实现优先级队列,是解决Top-K问题和求解最大/最小元素的常用工具。 9. **贪心算法**:在每一步选择局部最优解,以期望达到全局最优,如霍夫曼编码、活动安排问题等。...

    java简单算法

    10. **堆数据结构**:Java中的PriorityQueue实现了堆,可以用于优先级队列和最大/最小堆操作,如Top-K问题、堆排序等。 通过学习和实践这些经典的Java算法,你可以提升编程技能,更好地解决实际问题,对于面试和...

    最新Java 基本算法代码示例+注释

    7. **堆数据结构**:最大堆和最小堆常用于实现优先队列,它们在Top-K问题、堆排序等场景下发挥作用。 8. **贪心算法**:贪心策略每次做出局部最优选择,期望最终得到全局最优解。如霍夫曼编码、Prim和Kruskal的最小...

    今日头条2017校园招聘 java后台岗位面试题(1).pdf

    在Java中,PriorityQueue实现了最小堆功能,常用于优先级队列和Top-K问题。 6. **HTTP协议**:掌握HTTP的基本概念,如请求方法、状态码、报文结构,以及头部信息的使用,对于后端开发人员来说是必备的。 7. **AJAX...

    数据结构(Java版)电子教案

    Java的PriorityQueue类基于堆实现,常用于优先级队列和Top-K问题。 10. **字符串**:虽然字符串不是传统意义上的数据结构,但在处理文本数据时至关重要。Java的String类提供了丰富的字符串操作方法,而...

    阿里巴巴11

    《阿里巴巴11》这篇内容主要涉及了两个核心知识点:Top K问题的解决策略以及Java中的TreeMap实现原理。首先,我们来深入理解Top K算法。 Top K问题在数据处理中非常常见,目标是从大量数据中找出最大的K个元素。...

    优先级队列(堆实现)

    在实际编程中,`PriorityQueue`常被用作高效的解决方案,如解决Top-K问题、优先级调度等。例如,我们可以用优先级队列来实现一个简单的任务调度器,优先处理优先级高的任务。 总之,`PriorityQueue`通过堆数据结构...

    一亿取100数字Top100

    因此,高效的数据结构和算法成为解决问题的关键。 1. **哈希表**:这是解决这类问题的第一步,通常使用哈希表(HashMap或Dictionary)来存储每个数字及其出现的次数。哈希表的插入和查询操作平均时间复杂度为O(1),...

    java数据结构和算法

    ##### 1.2.6 TopK算法详细解析---百度面试 - **问题描述**:Top K 问题通常是指找出一组数据中最大的 K 个数或最小的 K 个数。 - **解决方案**: - 使用大顶堆(求最小的 K 个数)或小顶堆(求最大的 K 个数)的...

    剑指offer(java实现)

    - **堆**:一种特殊的树形数据结构,满足最大堆或最小堆性质,用于实现优先队列、Top-K问题等。 - **哈希表**:提供快速的插入、删除和查找操作,常用于实现字典、去重、最接近的k个数等问题。 2. **算法** - **...

    数据结构与算法(JAVA语言版)

    9. **堆**:一种特殊的树形数据结构,如最大堆和最小堆,常用于优先队列和Top K问题。 10. **散列表**:通过哈希函数实现快速查找,常用于缓存和去重。 在算法部分,书籍可能会包含以下主题: 1. **排序算法**:...

Global site tag (gtag.js) - Google Analytics