堆的应用:如何快速获取到TOP10最热门的搜索关键词?

堆的应用:如何快速获取到TOP10最热门的搜索关键词?

就是搜索引擎的热门搜索排行榜,搜索引擎每天会接收大量的用户搜索请求,他会把用户输入的搜索关键词记录下来,然后再离线统计分析,得到TOP10搜索关键词

假设我们有一个包含10亿个搜索关键词的日志文件,如何能快速获取到热门榜TOP10搜索关键词?

堆的应用一:优先级队列

优先级队列,首先是一个队列,队列的最大特性就是先进先出,不过,在优先级队列中,数据的出队顺序不是先进先出的,按照优先级来,优先级最高的,最先出队

用堆来实现一个优先级队列,因为堆和优先级队列非常相似,一个堆可以看作是一个优先级队列,往优先级队列中插入一个元素,相当于往堆里插入一个元素;从优先级队列中取出优先级最高的元素,相当于取出堆顶元素

这优先级应用场景很多:

一:合并有序小文件

有100个小文件,每个文件的大小是100MB,每个文件存储的都是有序的字符串,希望将这100个小文件合并成一个有序的大文件,用到优先级队列

从这100个文件中,各取第一个字符串,放入数组中比大小,把最小的那个字符串放入合并后的大文件中,并从数组中删除,这个最小的字符串来自于13.txt这个小文件,再从这个小文件取下一个字符串,放到数组中,重新比较大小,并且选择最小的放入合并后的大文件,将它从数组中删除,依此类推,直到所有的文件中的数据都放入到大文件中为止

用数组存储从小文件中取出来的字符串,每个从数组中取最小字符串,都需要循环遍历整个数组,不是很高效,可以用优先级队列,从小文件中取出来的字符串放入到小顶堆,那堆顶的元素就是优先级队列队首的元素,即最小的字符串,将这个字符串放入到大文件中,将其从堆中删除,然后再从小文件中取出下一个字符串,放入堆中,循环,可以将100个小文件中的数据依次放入大文件中

二:高性能定时器

定时器中维护了很多定时任务,每个任务都设定了一个要触发执行的时间点。定时器每过一个很小的单位时间,就扫描一遍任务,看是否有任务到达设定的执行时间,如果到达了,拿出执行

最好的方法是用优先级队列,按照任务设定的执行时间,将任务存储在优先级队列中,队列首部存储的是最先执行的任务,定时器只需要拿队首任务的执行时间点,与当前时间点相减,得到一个时间间隔T,定时器设定在T秒之后,再次执行任务,从当前时间点到(T-1)s之间不需要做任务事情

当T秒过去之后,定时器取优先级队列中队首的任务执行,再计算新的队首任务的执行时间点与当前时间点的差值

堆的应用二:利用堆求TOP k

求TOP K问题可以抽象成两类,一类是针对静态数据集合,即数据集合事先确定,不会再变,另一类是针对动态数据集合,数据集合事先并不确定,有数据动态加入集合中

针对静态数据,如何在包含n个数据的数组中,查找前K大数据呢?维护一个大小为K的小顶堆,顺序遍历数组,从数组中取出数据与堆顶元素比较,如果比堆顶元素大,把堆顶元素删除,将这个元素插入到堆中,如果比堆顶元素小,不处理,都遍历完之后,堆中的数据就是前K大数据

一次堆化操作需要O(logK),最坏的情况是n个元素都入堆一次,时间复杂度是O(nlogK)

针对动态数据求得TOP K就是实时TOP K,一个是添加数据,一个是询问当前TOP K

每次询问前K大数据,都一直维护一个K大小的小顶堆,当有数据被添加的时候,与堆顶元素对比

堆的应用三:利用堆求中位数

求动态数据集合中的中位数,即处在中间位置上的数

如果数据个数是奇数,把数据从小到大排列,第n/2+1个数据就是中位数(数据从0开始编号)如果数据个数是偶数的话,中间位置有两个,第n/2个和第n/2+1个数据,可以随意选取一个作为中位数

一组静态数据,中位数是固定的,可以先排序,第n/2个数据是中位数,每次询问中位数的时候,直接返回这个固定的值即可,边际成本会很小,但是,如果我们面对的是动态数据集合,中位数不断在变,如果先排序再询问的话效率就不高了

维护两个堆,一个大顶堆,一个小顶堆,大顶堆存储前半部分数据,小顶堆存储后半部分数据,且小顶堆中的数据都大于大顶堆中的数据,如果有n个数据,n是偶数,从小到大排序,前n/2个数据存储在大顶堆,后n/2个数据存储在小顶堆,这样,大顶堆堆顶元素就是中位数,如果n是奇数,大顶堆存储n/2+1个数据,小顶堆存储n/2个数据

数据是动态变化的,当新添加一个数据的时候,如何调整两个堆,让大顶堆的堆顶元素继续是中位数呢?

如果新加入的数据小于等于大顶堆的堆顶元素,则插入到大顶堆中,否则插入到小顶堆,这时候可能会出现两个顶堆的数据个数不符合前面约定的情况,所以我们可以从一个堆中不停地将堆顶元素移动到另一个堆,这样调整

可以利用两个堆实现动态数据集合中求中位数的操作,插入数据的时间复杂度为O(logn),求中位数只需要返回大顶堆的堆顶元素,时间复杂度是O(1)

利用两个堆不仅可以快速求出中位数,还可以求其他百分位的数据,如何利用两个堆快速求接口的99%响应时间?

99百分位数就是 将一组数据从小到大排列,这个99百分位数就是大于前面99%数据的那个数据,那99%响应时间,如果有100个接口访问请求,每个接口请求的响应时间都不同,然后将响应时间从小到大排列,排在第99的那个数据就是99%响应时间,也叫99百分位响应时间(ps:有n个数据,99百分位数就是第n*99%个数据)

求99%响应时间?

维护两个堆,一个大顶堆,一个小顶堆,当前总数据个数是n,大顶堆中保存 n 99 n*99% 个数据,小顶堆中保存 n 1 n*1% 个数据,大顶堆堆顶的数据就是我们要找的99%响应时间。每次插入一个数据的时候,判断这个数据跟大顶堆和小顶堆堆顶数据的大小关系,然后决定插入到那个堆中,如果该数据比大顶堆堆顶元素数据小,插入大顶堆,比小顶堆堆顶元素大,插入小顶堆,为了保持大顶堆中数据占99%,每次插入数据后,重新计算大顶堆和小顶堆中数据个数,是否符合99:1

加入有一个包含10亿个搜索关键词的日志文件,如何快速获取到TOP10最热门的搜索关键词?

可以使用MapReduce,如果我们将处理的场景限定为单机,内存是1GB,因为关键词很多都是重复的,首先统计每个关键词出现的频率,通过散列表、平衡二叉查找树等来记录关键词以及出现的次数

选用散列表,顺序扫描10亿搜索关键词,当扫描到某个关键词时,取散列表中查询,存在,次数+1,不存在,插入到散列表中,表中就存储了不重复的搜索关键词以及次数,再用堆求TOP K的方法,建立一个大小为10的小顶堆,遍历散列表,依次取出每个关键词及次数,与堆顶关键词对比,如果该次数比堆顶关键词次数多,删除堆顶关键词,将这个出现次数更多的关键词加入堆中

如果10亿条搜索关键词中不重复的有1亿条,每个关键词的平均长度是50bit,存储1亿个关键词需要5GB内存空间,而我们只有1GB,无法一次性将所有关键词加入内存中,怎么办?

相同数据经过哈希算法得到的哈希值是一样的,将10亿关键词先通过哈希算法分片到10个文件中,创建10个空文件00,01,02,……09遍历10亿关键词,通过某个哈希算法对其求哈希值,然后哈希值同10取模,得到的结果就是这个搜索关键词应该被分到的文件编号,每个文件都只有1亿关键字,去掉重复的,可能只有1000W个,总大小就是500MB,针对1亿关键词的文件,利用散列表和堆,分别求出TOP10,把10个TOP10放在一起,取这100个关键词中出现次数最多的10个关键词

怎么实现一个访问量非常大的新闻网站,希望将点击量排名TOP10的新闻摘要,滚动显示在网站首页banner上,每隔1小时更新一次?

对每个新闻摘要都计算一个hashcode,建立摘要和hashcode之间的联系,使用map存储,以hashcode为key,新闻摘要为值,每小时一个文件的方式记录下被点击的摘要的hashcode,当一个小时结束后,计算上一个小时的点击top10,将hashcode分片到多个文件,通过对hashcode取模也能算,将相同hashcode放到同一个文件中,针对每个文件取TOP10d hashcode,使用map<hashcode,int>的方式,统计所有的摘要点击次数,在使用小顶堆计算TOP10,针对所有分片计算一个总的TOP10,如果仅展示一个小时的,计算结束,展示全天的,与上次按hashcode合并,取TOP10

发布了76 篇原创文章 · 获赞 9 · 访问量 9191

猜你喜欢

转载自blog.csdn.net/ywangjiyl/article/details/104432130