海量数据小内存!如何找到高频数

文章目录

题目

如何在 20 亿个无符号整数中找到出现次数最多的那个数,在只提供 1 G 内存的条件下

解答

找到出现次数最多的数,通常的思维就是使用 HashMap 来统计这 20 亿个无符号整数中每个数出现的次数

已知只有 20 亿个数,那么我们使用 int 类型就足以来统计这些数了,那么一条 Hash 表记录 key 需要 4 个字节,value 需要 4 个字节,一条记录至少 8 个字节。最坏情况下,这 20 亿个数都是不同的,那么就需要 160 亿字节,即 16 G,在题目提供的 1 G内存条件下,显然是没有办法解决问题的

在这里插入图片描述

我们知道 Hash 函数,调完之后的结果分布具有离散性和均匀性这样的性质,并且同一个值调用同一个 Hash 函数,最后得到的结果一定是一样的

可以将这 20 亿个数分别进行调用 Hash 函数,然后将产生的结果 %100,最后得到的值的范围就是 0 ~ 99,将结果为 i 的数存放到文件 Ai 中。遍历就结束后,我们就可以得到 100 个小文件。因为调用过 Hash 函数后,结果是比较均匀的,所以每个小文件的数通过 HashMap 统计词频所占内存空间大小最多 0.16 G,妥妥的比 1 G小,如果不幸仍有超过 1 G,就采用相同的方式将 20 亿个数分解到更多的小文件中

根据 Hash 函数的性质,此时小文件中含有不同的数字种类是差不多的,相同的数一定被放在了同一个文件夹中

之后的事就非常好办了,我们只需要将小文件里的数通过 HashMap 进行频率统计,每个文件中都会决出一个出现次数最多的数字,一个文件统计完后,就可以释放内存,最后 100 个小文件产出的结果中在做对比,产出最后的结果,即出现次数最多的数

试问:如果我们想要从这 20 亿个数中找到频率最高的 100 个数,还是 1 G内存怎么找?

前面的将每个数进行调用 Hash 函数,%100,分成 100 个小文件的操作是一样的,只不过每个小文件中频率统计的操作中,我们需要依次遍历每个小文件,构建一个大小为 100 的小根堆

如果遍历到的数出现的次数大于堆顶数出现的次数,那么就让新的数顶替堆顶的数,重新调整为小根堆,自然,遍历结束后,小根堆上的数就是出现频率最高的 100 个数

为了提高一定效率,或许我们还可以这样做…

先将每个小文件中的信息分别放到各自的大根堆(大小为100)中,产生了小文件中的 TOP 100,堆顶的数自然就是这个文件中出现最多的数

然后再准备一个总的大根堆,将各个小文件的大根堆的堆顶信息弹出,放到总的大根堆中。此时,总堆的堆顶就是所有文件中出现次数最多的数,将其弹出放到结果保存文件中,然后查看方才保存的那个数是来自哪个小文件的大根堆。

比如来自 ② 号文件的大根堆,由于之前已经把该文件中出现次数最多的数弹出了,所以此时 ② 号文件的大根堆的堆顶就是原来文件中出现次数第二多的数,将其放到总的堆中。

然后继续之前的操作,将总堆堆顶的元素弹出进行保存,以此类推,最后找到了 20 亿个数中出现频率最多的 TOP 100

总结

  • 分而治之,将大数据进行哈希取余,从而分成小文件
  • 使用 HashMap 频率统计
  • 将数据整合比对
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

富春山居_ZYY(已黑化)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值