转载自: doocs/advanced-java
1. 如何从大量URL中找出相同的URL?
题目描述:
给定 a、b 两个文件,各存放 50 亿个 URL,每个 URL 各占 64B,内存限制是 4G。请找出 a、b 两个文件共同的 URL。
总体思路:
- 分而治之,进行哈希取余。
- 对每个子文件进行HashSet统计。
- 分而治之,进行哈希取余。
解答思路:
题目描述:
有一个 1GB 大小的文件,文件里每一行是一个词,每个词的大小不超过 16B,内存大小限制是 1MB,要求返回频数最高的 100 个词(Top 100)。
总体思路:
- 分而治之,哈希取余
- 使用hashmap统计频数
- 使用小顶堆求TOP100
- 分而治之,哈希取余
解答思路:
题目描述:
现有海量日志数据保存在一个超大文件中,该文件无法直接读入内存,要求从中提取某天访问百度次数最多的那个 IP。
总体思路:
- 分而治之,哈希取余
- 使用hashmap统计频数
- 求max
- 分而治之,哈希取余
解答思路:
题目描述:
在2.5亿个整数中找出不重复的整数。
总体思路:
- 分治法或位图法。
解答思路:
题目描述:
给定 40 亿个不重复的没排过序的 unsigned int 型整数,然后再给定一个数,如何快速判断这个数是否在这 40 亿个整数当中?
总体思路:
- 分治法或位图法
- 分治法或位图法
解答思路:
题目描述:
搜索引擎会通过日志文件把用户每次检索使用的所有查询串都记录下来,每个查询串的长度不超过 255 字节。 假设目前有 1000w 个记录(这些查询串的重复度比较高,虽然总数是 1000w,但如果除去重复后,则不超过 300w 个)。请统计最热门的 10 个查询串,要求使用的内存不能超过 1G。(一个查询串的重复度越高,说明查询它的用户越多,也就越热门。)
总体思路:
- 分治法或HashMap法或前缀树法。
- 分治法或HashMap法或前缀树法。
解答思路:
- 每个查询串最长为 255B,1000w 个串需要占用 约 2.55G 内存,因此,我们无法将所有字符串全部读入到内存中处理。
- 分治法: 划分为多个小文件,保证单个小文件中的字符串能被直接加载到内存中处理,然后求出每个文件中出现次数最多的 10 个字符串;最后通过一个小顶堆统计出所有文件中出现最多的 10 个字符串。
- HashMap法: 虽然字符串总数比较多,但去重后不超过300w,因此可以考虑把所有字符串以及出现次数保存在一个hashmap中。所占用的空间为 300w*(255+4)≈777M(其中,4 表示value为整数占用的 4 个字节)。由此可见,1G 的内存空间完全够用。然后用小顶堆统计出hashmap中出现次数最多的10个字符串。
- 前缀树法:这些字符串含有大量相同的前缀时,可以考虑使用前缀树来统计字符串出现次数,树的结点保存字符串出次数,0表示没有出现。
- 具体步骤: 在遍历字符串时,在前缀树中查找,如果找到,则把结点中保存的字符串次数+1,否则为这个字符串构建新节点。构建完后把叶子结点中字符串的出现次数置为1.最后依然使用小顶堆来对字符串的出现次数进行排序。
如何统计不同电话号码的个数?
- 每个查询串最长为 255B,1000w 个串需要占用 约 2.55G 内存,因此,我们无法将所有字符串全部读入到内存中处理。
题目描述:
已知某个文件内包含一些电话号码,每个号码为 8 位数字,统计不同号码的个数。
总体思路:
- 位图法
- 位图法
解答思路:
题目描述:
从 5 亿个数中找出中位数。数据排序后,位置在最中间的数就是中位数。当样本数为奇数时,中位数为 第
(N+1)/2
个数;当样本数为偶数时,中位数为 第N/2
个数与第1+N/2
个数的均值。总体思路
- 双堆法或分治法。
- 双堆法或分治法。
解答思路:
- 双堆法:维护两个堆,一个大顶堆,一个小顶堆。大顶堆中的最大数小于等于小顶堆中最小的数。保证这两个堆中的元素个数差不超过1。具体见
leetcode 295
。若数据总数为偶数,当这两个堆建好之后,中位数就是这两个堆顶元素的平均值。当数据总数为奇数时,根据两个堆的大小,中位数一定在数据多的堆的堆顶。 - 分治法:顺序读取这5亿个数字,对于读取到的数字num,如果它对应的二进制中最高位为1,则把这个数字写到f1中,否则写到f0中。在最高位是符号位的情况下,f0的数全部大于f1。划分后,可以非常容易的知道中位数是在f0还是f1中。假设f1中有1亿个数,那么中位数一定在f0中,且是在f0中,从小到大排列的1.5亿个数与它后面一个数的平均值。
- 对于f0可以用次高位的二进制继续将文件一分为二,如此划分下去,直到划分后的文件可以被加载到内存中,把数据加载到内存中后可以直接排序,找出中位数。
如何按照query的频度排序?
- 双堆法:维护两个堆,一个大顶堆,一个小顶堆。大顶堆中的最大数小于等于小顶堆中最小的数。保证这两个堆中的元素个数差不超过1。具体见
题目描述:
有 10 个文件,每个文件大小为 1G,每个文件的每一行存放的都是用户的 query,每个文件的 query 都可能重复。要求按照 query 的频度排序。
总体思路:
- 内存若够,直接读入进行排序;
- 内存不够,先划分为小文件,小文件排好序后,整理使用外排序进行归并。
- 内存若够,直接读入进行排序;
解答思路:
- 如果 query 的重复度比较大,可以考虑一次性把所有 query 读入内存中处理;如果 query 的重复率不高,那么可用内存不足以容纳所有的 query,这时候就需要采用分治法或其他的方法来解决。
- HashMap法:如果 query 重复率高,说明不同 query 总数比较小,可以考虑把所有的 query 都加载到内存中的 HashMap 中。接着就可以按照 query 出现的次数进行排序。
- 分治法:分治法需要根据数据量大小以及可用内存的大小来确定问题划分的规模。对于这道题,可以顺序遍历 10 个文件中的 query,通过 Hash 函数
hash(query) % 10
把这些 query 划分到 10 个小文件中。之后对每个小文件使用 HashMap 统计 query 出现次数,根据次数排序并写入到零外一个单独文件中。接着对所有文件按照 query 的次数进行排序,这里可以使用归并排序(由于无法把所有 query 都读入内存,因此需要使用外排序)。
如何找出排名前500的数?
- 如果 query 的重复度比较大,可以考虑一次性把所有 query 读入内存中处理;如果 query 的重复率不高,那么可用内存不足以容纳所有的 query,这时候就需要采用分治法或其他的方法来解决。
题目描述:
有 20 个数组,每个数组有 500 个元素,并且有序排列。如何在这 20*500 个数中找出前 500 的数?
解答思路:
题目描述:
一个文件中有9亿条不重复的9位整数,对这个文件中数字进行排序;
解答思路:
- 分治法:过Hash法将9亿条数据分为20个小文件,每个文件存5000万条,大约需要占用5000万*4B = 200MB空间。遍历这20个小文件,每次取500万条数据,进行排序,将排序结果放入一个大文件中。 一共要进行5000/500=10次排序。
- 位图法:考虑到最大的9位整数为999999999,由于9亿条数据是不重复的,可以声明一个可以包含9位整数的bit数组,一共需要10亿/8,大约120MB内存。把内存中的数组全部初始化为0,读取文件中的数据,并将数据放入内存,遍历所有数据,将这9亿条数据放入bit数组中,对应bit置一。最后遍历整个bit数组,将bit为1的数组下标存入文件,最终得到排序的内容.
- 分治法:过Hash法将9亿条数据分为20个小文件,每个文件存5000万条,大约需要占用5000万*4B = 200MB空间。遍历这20个小文件,每次取500万条数据,进行排序,将排序结果放入一个大文件中。 一共要进行5000/500=10次排序。