海量数据题归总

无重复海量数据求排序

  1. 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

  1. 按照IP地址的Hash(IP)%1024值,把海量IP日志分别存储到1024个小文件中。这样,每个小文件最多包含4MB个IP地址;
  2. 对于每一个小文件,可以构建一个IP为key,出现次数为value的Hash map,同时记录当前出现次数最多的那个IP地址;
  3. 可以得到1024个小文件中的出现次数最多的IP,再依据常规的排序算法得到总体上出现次数最多的IP;

多个文件海量数据排序

例题:有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。

法一:

  1. 顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(记为)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
  2. 找一台内存在2G左右的机器,依次对用hash_map(query,query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(记为)。
  3. 对这10个文件进行归并排序(内排序与外排序相结合)。
    (可是这一步的话,不会涉及到所有文件读入内存吗??)
    答:因为有排序,所以可以避免需要一次读入全部的情况

法二:
做完hash,分成多个文件后,可以交给多个文件来处理,采用分布式的架构来处理(比如MapReduce),最后再进行合并。

两个大文件求重复的记录

例题:给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

法一:

  1. 遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,...,a999)中。这样每个小文件的大约为300M。
  2. 遍历文件b,采取和a相同的方式将url分别存储到1000小文件(记为b0,b1,...,b999)。
  3. 这样处理后,所有可能相同的url都在对应的小文件(a0vsb0,a1vsb1,...,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。

法二:
如果允许有一定的错误率,可以使用Bloom filter

海量数据求不重复出现的记录。

例题:在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的整数输出即可。

法二:
也可采用与第1题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。(因为有排序,所以可以避免需要一次读入全部的情况)然后再进行归并,注意去除重复的元素。

海量数据中查找

例题:腾讯面试题:给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?

法一:
申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。
(然而这样的话好像并没有快在哪里,只是后续多次查询可能加快)

法二:
(编程珠玑里面的方法)
(应该算是二分查找的思想)
2^32为40亿多,所以给定一个数可能在,也可能不在其中;
这里我们把40亿个数中的每一个用32位的二进制来表示
假设这40亿个数开始放在一个文件中。

然后将这40亿个数分成两类:

  1. 最高位为0
  2. 最高位为1

并将这两类分别写入到两个文件中,其中一个文件中数的个数<=20亿,而另一个>=20亿(这相当于折半了);
与要查找的数的最高位比较并接着进入相应的文件再查找

再然后把这个文件为又分成两类:

  1. 次最高位为0
  2. 次最高位为1

并将这两类分别写入到两个文件中,其中一个文件中数的个数<=10亿,而另一个>=10亿(这相当于折半了);
与要查找的数的次最高位比较并接着进入相应的文件再查找。

.......

以此类推,就可以找到了,而且时间复杂度为O(logn)

参考资料

  1. 编程珠玑
  2. http://blog.csdn.net/v_JULY_v
posted @ 2017-04-18 10:46  cswxa  阅读(273)  评论(0编辑  收藏  举报