原文:http://blog.sina.com.cn/s/blog_466678e801011fif.html
前言
第一部分、十五道海量数据处理面试题
1. 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
- 遍历文件a,对每个url求取
,然后根据所取得的值将url分别存储到1000个小文件(记为
)中。这样每个小文件的大约为300M。
- 遍历文件b,采取和a相同的方式将url分别存储到1000小文件中(记为
)。这样处理后,所有可能相同的url都在对应的小文件(
)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
- 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
- hash后要判断每个文件大小,如果hash分的不均衡有文件较大,还应继续hash分文件,换个hash算法第二次再分较大的文件,一直分到没有较大的文件为止。这样文件标号可以用A1-2表示(第一次hash编号为1,文件较大所以参加第二次hash,编号为2)
- 由 于1存在,第一次hash如果有大文件,不能用直接set的方法。建议对每个文件都先用字符串自然顺序排序,然后具有相同hash编号的(如都是1-3, 而不能a编号是1,b编号是1-1和1-2),可以直接从头到尾比较一遍。对于层级不一致的,如a1,b有1-1,1-2-1,1-2-2,层级浅的要和 层级深的每个文件都比较一次,才能确认每个相同的uri。
2. 有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。
方案1:
- 顺序读取10个文件,按照hash(query)的结果将query写入到另外10个文件(记为
)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
- 找一台内存在2G左右的机器,依次对
用hash_map(query, query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为
)。
- 对
这10个文件进行归并排序(内排序与外排序相结合)。
方案2:
方案3:
3. 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
,然后按照该值存到5000个小文件(记为
) 中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,直到分解得到的小文件的大小都不超过1M。对 每个小文件,统计每个文件中出现的词以及相应的频率(可以采用trie树/hash_map等),并取出出现频率最大的100个词(可以用含100个结点 的最小堆),并把100词及相应的频率存入文件,这样又得到了5000个文件。下一步就是把这5000个文件进行归并(类似与归并排序)的过程了。
4. 海量日志数据,提取出某日访问百度次数最多的那个IP。
5. 在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。
6. 海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。
方案1:
- 在 每台电脑上求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆)。比如求TOP10大,我们首先取前10 个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元素就 是TOP10大。
- 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。
(更多可以参考:第三章、寻找最小的k个数,以及第三章续、Top K算法问题的实现)
第6题的方法中,是不是不能保证每个电脑上的前十条,肯定包含最后频率最高的前十条呢?
比如说第一个文件中:A(4), B(5), C(6), D(3)
第二个文件中:A(4),B(5),C(3),D(6)
第三个文件中: A(6), B(5), C(4), D(3)
如果要选Top(1), 选出来的结果是A,但结果应该是B。
7. 怎么在海量数据中找出重复次数最多的一个?
8. 上千万或上亿数据(有重复),统计其中出现次数最多的钱N个数据。
9. 1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
10. 一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。
11. 一个文本文件,找出前10个经常出现的词,但这次文件比较长,说是上亿行或十亿行,总之无法一次读入内存,问最优解。
12. 100w个数中找出最大的100个数。
-
方案1:采用局部淘汰法。选取前100个元素,并排序,记为序列L。然后一次扫描剩余的元素x,与排好序的100个元素中最小的元素比,如果比这个最小的 要大,那么把这个最小的元素删除,并把x利用插入排序的思想,插入到序列L中。依次循环,知道扫描了所有的元素。复杂度为O(100w*100)。 -
方案2:采用快速排序的思想,每次分割之后只考虑比轴大的一部分,知道比轴大的一部分在比100多的时候,采用传统排序算法排序,取前100个。复杂度为O(100w*100)。 -
方案3:在前面的题中,我们已经提到了,用一个含100个元素的最小堆完成。复杂度为O(100w*lg100)。
13. 寻找热门查询:
搜 索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较 高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询 串,要求使用的内存不能超过1G。
(1) 请描述你解决这个问题的思路;
(2) 请给出主要的处理流程,算法,以及算法的复杂度。
14. 一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到N^2个数中的中数?
15. 最大间隙问题
给定n个实数,求着n个实数在实轴上向量2个数之间的最大差值,要求线性的时间算法。
方案1:最先想到的方法就是先对这n个数据进行排序,然后一遍扫描即可确定相邻的最大间隙。但该方法不能满足线性时间的要求。故采取如下方法:
- 找到n个数据中最大和最小数据max和min。
- 用n-2个点等分区间[min, max],即将[min, max]等分为n-1个区间(前闭后开区间),将这些区间看作桶,编号为
,且桶i 的上界和桶i+1的下届相同,即每个桶的大小相同。每个桶的大小为:
。实际上,这些桶的边界构成了一个等差数列(首项为min,公差为
),且认为将min放入第一个桶,将max放入第n-1个桶。
- 将n个数放入n-1个桶中:将每个元素x[i] 分配到某个桶(编号为index),其中
,并求出分到每个桶的最大最小数据。
- 最 大间隙:除最大最小数据max和min以外的n-2个数据放入n-1个桶中,由抽屉原理可知至少有一个桶是空的,又因为每个桶的大小相同,所以最大间隙不 会在同一桶中出现,一定是某个桶的上界和气候某个桶的下界之间隙,且该量筒之间的桶(即便好在该连个便好之间的桶)一定是空桶。也就是说,最大间隙在桶i 的上界和桶j的下界之间产生j>=i+1。一遍扫描即可完成。
16. 将多个集合合并成没有交集的集合
。要求将其中交集不为空的集合合并,要求合并完成的集合之间无交集,例如上例应输出
。
(1) 请描述你解决这个问题的思路;
(2) 给出主要的处理流程,算法,以及算法的复杂度;
(3) 请描述可能的改进。
, 首先查看aaa和bbb是否在同一个并查集中,如果不在,那么把它们所在的并查集合并,然后再看bbb和ccc是否在同一个并查集中,如果不在,那么也把 它们所在的并查集合并。接下来再扫描其他的集合,当所有的集合都扫描完了,并查集代表的集合便是所求。复杂度应该是O(NlgN)的。改进的话,首先可以 记录每个节点的根结点,改进查询。合并的时候,可以把大的和小的进行合,这样也减少复杂度。
17. 最大子序列与最大子矩阵问题
数组的最大子序列问题:给定一个数组,其中元素有正,也有负,找出其中一个连续子序列,使和最大。
。基于这一点可以很快用代码实现。
最大子矩阵问题:给定一个矩阵(二维数组),其中数据有大有小,请找一个子矩阵,使得子矩阵的和最大,并输出这个和。
第二部分、海量数据处理之Bti-map详解
什么是Bit-map
然后再处理第二个元素7,将第八位置为1,,接着再处理第三个元素,一直到最后处理完所有的元素,将相应的位置为1,这时候的内存的Bit位的状态如下:
然后我们现在遍历一遍Bit区域,将该位是一的位的编号输出(2,3,4,5,7),这样就达到了排序的目的。下面的代码给出了一个BitMap的用法:排序。
- //定义每个Byte中有8个Bit位
- #include
<memory.h> - #define
BYTESIZE 8 - void
SetBit(char *p, int posi) - {
-
for(int i=0; i < (posi/BYTESIZE); i++) -
{ -
p++; -
} -
-
*p = *p|(0x01<<(posi%BYTESIZE));//将该Bit位赋值1 -
return; - }
-
- void
BitMapSortDemo() - {
-
//为了简单起见,我们不考虑负数 -
int num[] = {3,5,2,10,6,12,8,14,9}; -
-
//BufferLen这个值是根据待排序的数据中最大值确定的 -
//待排序中的最大值是14,因此只需要2个Bytes(16个Bit) -
//就可以了。 -
const int BufferLen = 2; -
char *pBuffer = new char[BufferLen]; -
-
//要将所有的Bit位置为0,否则结果不可预知。 -
memset(pBuffer,0,BufferLen); -
for(int i=0;i<9;i++) -
{ -
//首先将相应Bit位上置为1 -
SetBit(pBuffer,num[i]); -
} -
-
//输出排序结果 -
for(int i=0;i<BufferLen;i++)//每次处理一个字节(Byte) -
{ -
for(int j=0;j<BYTESIZE;j++)//处理该字节中的每个Bit位 -
{ -
//判断该位上是否是1,进行输出,这里的判断比较笨。 -
//首先得到该第j位的掩码(0x01<<j),将内存区中的 -
//位和此掩码作与操作。最后判断掩码是否和处理后的 -
//结果相同 -
if((*pBuffer&(0x01<<j)) == (0x01<<j)) -
{ -
printf("%d ",i*BYTESIZE + j); -
} -
} -
pBuffer++; -
} - }
-
- int
_tmain(int argc, _TCHAR* argv[]) - {
-
BitMapSortDemo(); -
return 0; - }
可进行数据的快速查找,判重,删除,一般来说数据范围是int的10倍以下
基本原理及要点
使用bit数组来表示某些元素是否存在,比如8位电话号码
扩展
Bloom filter可以看做是对bit-map的扩展(关于Bloom filter,请参见:海量数据处理之Bloom filter详解)。
问题实例
1)已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。
2)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
参考: