堆的应用
优先级队列
按优先级来,优先级最高的,最先出队。可以用堆来实现。
- 合并有序小文件 假设有100个大小为100MB小文件,每个文件中存储的都是有序的字符串。如何将这100个小文件合并成一个有序大文件。
思路:同样采用合并两个有序数组的方式。从100个文件中各取第一个字符串,放入一个数组中比较大小,把最小的字符串放入合并后的大文件中,并从比较大小的数组中删除。然后在从最小字符串的来源文件再取一个字符串放入数组中比较大小,依次进行直到一个文件字符取完,就只比较剩余的文件。
主要问题就是取100个元素中最小的元素,可以使用小顶堆排序,每次删除堆顶元素和插入新元素。这样更为高效。
- 高效能定时器
在一个定时器中会维护很多定时认为,每个人物都设定一个触发执行时间,定时器每隔1s扫描一遍任务列表,查看此时应该触发哪个任务执行。
但是这种方式明显很低效:1. 当前并没有要触发的任务;2. 任务列表很大,扫描耗时。
因此我们可以把最近的时间点(时间最小值)任务拿出来,每次只扫描最近的任务,查看此时与任务触发时间的差值T,再间隔时间T后再执行任务,同时删除该任务,查找剩余最小值。
这个过程也就联想到了小顶堆,把任务按照触发时间排成小顶堆,每次扫描执行堆顶,删除堆顶。
利用堆求Top K
该问题有两类:一类针对静态数据集合(数据不变)。一类针对动态数据集合(数据是变动的)。
- 静态数据集合 在n数据的数组中,查找前K大数据?
维护一个大小为K的小顶堆(最终让它存储最大的K个元素),因此每次从数组中取一个元素和小顶堆堆顶比较:< 不处理,继续遍历数组;>插入小顶堆中。等数组遍历完成,也就找到了前K大数据。
最坏的时间复杂度O(nlogK)。
利用堆求中位数
中位数:处在中间位置的数据。 如果数据是静态的,那么求中位数其实就是求第n/2小的数据,也就是求index=n/2的元素,那么用快速排序的思想就可以做了。
如果数据是动态的,不能每次数据变化就排序一次太低效,因此可以使用堆。
以此类推,两个堆不仅可以就觉求中位数的问题,还可以快速求出其他百分位的数据,原理类似。
习题
在一个包含10亿个搜索关键字的日志文件中如何快速获取到Top10最热(搜索次数最多)关键词。
TopN想到使用前面的堆排序来做,但是10亿个关键词,肯定没法一下子都加在到内存中,因此我们联想到之前哈希函数时的应用分布式。那如果我不讲数据分到不同的计算机中,仅仅把10亿数据分片到10个文件中也是一样的。同样使用哈希函数求余数的方式将10亿数据分到10个文件中。再在这个文件中利用字典(或者其他散列表key(关键字)-value(出现次数))。之后再使用大小为10的最小堆,分别对10个文件逐个比较计算,直到全部比较完。
如果数据是实时更新的怎么办?
按照分片的规则把新数据插入到合适的字典中,再拿这个数据的value去和堆顶比较。