堆和堆排序的应用

优先级队列

优先级队列与普通队列的区别在于是按照优先级的顺序来出队。
赫夫曼编码、图的最短路径、最小生成树算法等都依赖优先级队列。

具体应用场景
1.合并有序小文件
假设有100个100MB的小文件,每个文件都是有序的字符串,将其合并成一个有序大文件。
将每个小文件的最小字符串放入一个小顶堆中,从小顶堆中取出堆顶并放入结果文件中。并将堆顶所在的小文件取出下一个字符串放入堆顶并堆化(循环如此)。

2.高性能定时器
有一个定时执行任务的表格:


image.png

如果每秒扫描一次这个表格,如果到达时间则执行。这是比较低效的:任务扫描时间可能过长,很多扫描都是徒劳的。
我们可以使用优先队列,使用小顶堆,只需要找出小顶堆的堆顶,定时去只需要在预定时间之后直接去执行堆顶任务并重新堆化即可。

Top K

1.静态数据:只需要顺序遍历数组,从数组中取出数据与堆(小顶堆)顶元素比较即可,如果大于堆顶,则删除堆顶并将这个元素插入堆顶。如果比堆顶元素小则不作处理。
时间复杂度:遍历数组为O(n),一次堆化时间为O(logK),最坏情况n个元素都入堆一次,时间复杂度为O(nlogK)
2.动态数据:持续维护一个K个大小的小顶堆,查询时直接返回堆即可。

利用堆求中位数

n个数据取出排序的中位数
1.静态数据:可以先排序后取出第2/n​ 个数据即可。
2.动态数据:维护两个堆,一个大顶堆(维护数据前一半的数据),一个小顶堆(维护数据后一半的数据)。如果有 n 个数据,n 是偶数,我们从小到大排序,那前 2/n​ 个数据存储在大顶堆中,后 2n​ 个数据存储在小顶堆中。这样,大顶堆中的堆顶元素就是我们要找的中位数。如果 n 是奇数,情况是类似的,大顶堆就存储 2/n​+1 个数据,小顶堆中就存储 2/n​ 个数据。
如果动态添加数据的话会比较大顶堆和小顶堆的堆顶并根据结果插入到某个堆中,但是这样会导致两个堆无法保证中位数的分布情况,所以需要调整堆,将某个堆中的堆顶移动到另一个堆的堆顶。

如果不是中位数,而是99%
道理与中位数相同,只不过会在更新数据后算出两个堆的数据个数所占比例并按照比例去调整堆的大小

假设现在我们有一个包含 10 亿个搜索关键词的日志文件,如何快速获取到 Top 10 最热门的搜索关键词

可以使用MapReduce
如果是单机处理并且内存不足以把10亿数据放入内存的时候。
按照关键词放入散列表中,value为次数。之后遍历散列表后使用小顶堆得到Top K。但是这也会导致内存不足。
所以可以将10亿个关键词按照哈希值划分到10个空文件中,并对每个文件(500MB)取出Top 10,并将10个 Top 10,放在一块并得到最终Top 10

你可能感兴趣的:(堆和堆排序的应用)