在处理海量数据时,遇到的主要问题有两个:
(1)时间问题:如何在短时间内高效地处理数据,一般是采用巧妙的算法搭配合适的数据结构;
(2)空间问题:如何在有限的内存中处理海量数据,一般是采用分而治之的思想,将海量数据划分为小份的数据;
(一)分而治之+统计+排序
1.分而治之
(1)一般是采用hash映射的方法,将海量数据划分为若干小份量的数据;
(2)常见做法:hash(X)%N;其中X为数据项,N为小份量数据的数量;
2.统计
(1)一般采用hash统计的方法;
(2)常见做法:hash_map(key,value)
3.排序
(1)一般采用堆排序,快速排序或者归并排序;
(2)当问题为寻找topK时,则可使用优化的堆排序:使用容量为k的最小堆存储最先遍历到的k个数,并假设它们即是最大的k个数,建堆费时O(k),并调整堆(费时O(logk))后,有k1>k2>...kmin(kmin设为小顶堆中最小元素);继续遍历数列,每次遍历一个元素x,与堆顶元素比较,若x>kmin,则更新堆(用时logk),否则不更新堆;这样下来,总费时O(k*logk+(n-k)*logk)=O(n*logk);此方法得益于在堆中,查找等各项操作时间复杂度均为logk;
4.常见例子
(1)从海量日志数据中提取出某日访问百度次数最多的那个IP
1)hash映射:hash(IP)%1000;将IP分别存储到1000个小文件中;
2)hash统计:hash_map(IP,value);统计出每个小文件中每个IP出现的频数;
3)排序:堆排序;从每个小文件中找出出现频数最高的IP,再从找出的1000个IP中找出出现频数最高的IP;
(2)从存有1kw个检索串的搜索引擎日志中找出出现频率最高的前10个检索串
1)hash映射:由于文件不大,可以内存处理,因此不需要做hash映射;
2)hash统计:hash_map(query,value);统计每个query出现的频数;
3)排序:堆排序找top10;
(3)有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M,返回频数最高的100个词
1)hash映射:hash(x)%5000;将文件分割为5000个小文件;
2)hash统计:hash_map(term,value);在小文件中统计每个词出现的频数;
3)排序:对小文件做堆排序找top100,得到5000个文件,对5000个文件做归并排序;
(二)Bit-map
1.所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素;由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省;
PS:上述可以表示数组2,3,4,5,7
2.常见例子
(1)在2.5亿个整数中找出不重复的整数;已知内存不足以容纳这2.5亿个整数
1)采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32 * 2 bit=1 GB内存,还可以接受;
2)然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变;所描完事后,查看bitmap,把对应位是01的整数输出即可;
(2)给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中
1)申请512M的内存,一个bit位代表一个unsigned int值;
2)读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在;
(三)Bloom-filter(布隆过滤器)
1. Bloom Filter是一种空间效率很高的随机数据结构,它的原理是,当一个元素被加入集合时,通过K个Hash函数将这个元素映射成一个位阵列(Bit array)中的K个点,把它们置为1;检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检索元素一定不在;如果都是1,则被检索元素很可能在;这就是布隆过滤器的基本思想;但Bloom Filter的这种高效是有一定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive);因此,Bloom Filter不适合那些“零错误”的应用场合;而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省;
2.改进:对于Bloom Filter来说原理很简单,位数组+k个独立hash函数;将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,很明显这 个过程并不保证查找的结果是100%正确的,同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字;所以一个简单的改进就是 counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了;
3.常见例子
(1)给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL
1)4G=2^32大概是40亿*8大概是340亿;4G内存大概可以表示340亿bit;
2)将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否在Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率);
(四)前缀树与后缀树
1.前缀树
(1)概念:是一种树形结构,是一种哈希树的变种;典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计;它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高;
(2)核心思想:空间换时间;利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的;
(3)基本性质:根节点不包含字符,除根节点外每一个节点都只包含一个字符;从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;每个节点的所有子节点包含的字符都不相同;
存储单词:b,abc,abd,bcd,abcd,efg,hii
2.后缀树:存有一个字符串所有后缀的压缩的前缀树;