海量数据题归总
无重复海量数据求排序
- bitmap法。
例题:电话号码排序
有重复海量数据求排序
例题:有重复的电话号码排序
可以把电话号码当成大整数。
自己猜想解决方案:
法一:
外排序。切分为多个小文件,对小文件内排序。再归并各小文件。(缺陷是需要大量的IO,消耗时间,效率不高)
法二:
应该有更高效的方法。
海量数据求重复次数最多的top k记录
注意这种求出现次数top k的问题还是要转化为求先求出每个记录出现的次数后,再求top k大。top k大的话则用小根堆比较方便了。
法一: (假如这些数据能放进内存的情况下)
①先对这批海量数据预处理,在O(N)的时间内用Hash表完成统计;
②借助堆这个数据结构,找出Top K,时间复杂度为NlogK。维护一个K大小的小根堆,然后遍历数据,分别和根元素进行对比所以,比根大就和根元素替换,再调整堆。我们最终的时间复杂度是:O(N) + N*O(logK)
法二:(假如内存不够放这些数据的Hash统计表)
应该也可以采用分治法,分成1000个文件,每个文件用法一求出top k个。再每个文件合并。
法三:
例如大文本文件中找词频最高的10个词,
可以使用字典树+小根堆的方法。
维护大小为10数组存储当前出现次数最高的词和它们的出现次数,按降序排列。每在二叉排序树中成功查找到该次,更新该词的出现次数后,与前10数组进行比较,如果这个词已经出现前10数组那就直接更新对应的值,如果这个词没有出现,则删除最有一个,然后插入。
是否还有其他方法?
附:top K问题的特殊例子: top 1
例如ip访问日志, 求出现频率最高的ip
算法思想:分而治之+Hash
- 按照IP地址的Hash(IP)%1024值,把海量IP日志分别存储到1024个小文件中。这样,每个小文件最多包含4MB个IP地址;
- 对于每一个小文件,可以构建一个IP为key,出现次数为value的Hash map,同时记录当前出现次数最多的那个IP地址;
- 可以得到1024个小文件中的出现次数最多的IP,再依据常规的排序算法得到总体上出现次数最多的IP;
多个文件海量数据排序
例题:有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。
法一:
- 顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(记为)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
- 找一台内存在2G左右的机器,依次对用hash_map(query,query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为)。
- 对这10个文件进行归并排序(内排序与外排序相结合)。
(可是这一步的话,不会涉及到所有文件读入内存吗??)
答:因为有排序,所以可以避免需要一次读入全部的情况
法二:
做完hash,分成多个文件后,可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce),最后再进行合并。
两个大文件求重复的记录
例题:给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
法一:
- 遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,...,a999)中。这样每个小文件的大约为300M。
- 遍历文件b,采取和a相同的方式将url分别存储到1000小文件(记为b0,b1,...,b999)。
- 这样处理后,所有可能相同的url都在对应的小文件(a0vsb0,a1vsb1,...,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
法二:
如果允许有一定的错误率,可以使用Bloom filter
海量数据求不重复出现的记录。
例题:在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。
法一:
- 采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32 * 2 bit=1 GB内存,还可以接受。
- 然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
法二:
也可采用与第1题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。(因为有排序,所以可以避免需要一次读入全部的情况)然后再进行归并,注意去除重复的元素。
海量数据中查找
例题:腾讯面试题:给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
法一:
申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。
(然而这样的话好像并没有快在哪里,只是后续多次查询可能加快)
法二:
(编程珠玑里面的方法)
(应该算是二分查找的思想)
2^32为40亿多,所以给定一个数可能在,也可能不在其中;
这里我们把40亿个数中的每一个用32位的二进制来表示
假设这40亿个数开始放在一个文件中。
然后将这40亿个数分成两类:
- 最高位为0
- 最高位为1
并将这两类分别写入到两个文件中,其中一个文件中数的个数<=20亿,而另一个>=20亿(这相当于折半了);
与要查找的数的最高位比较并接着进入相应的文件再查找
再然后把这个文件为又分成两类:
- 次最高位为0
- 次最高位为1
并将这两类分别写入到两个文件中,其中一个文件中数的个数<=10亿,而另一个>=10亿(这相当于折半了);
与要查找的数的次最高位比较并接着进入相应的文件再查找。
.......
以此类推,就可以找到了,而且时间复杂度为O(logn)