海量数据相关问题

方案一:分而治之/Hash映射(内存要求不够的时候,需要我们把文件中的数据分到不同的文件中) + Hash_map统计 + 堆/快速/归并排序

1、海量日志数据,提取出某日访问百度次数最多的那个IP。

首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法,比如%1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map对那1000个文件中的所有IP进行频率统计,然后依次找出各个文件中频率最大的那个IP)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。

计算内存:

 N=2^32约等于40亿

 40亿*(32/8)大概需要16G内存(一个ip地址需要32/8=4byte大小的数据)

 假设运行内存512M,512M/4=128M个ip地址(内存一次只能放入这么多ip地址)

 4G/128M=32(把ip地址分成32个区间段,计算每个段访问次数最多的ip地址)

 hash映射%32,映射到不同的小文件里面,然后用Hashmap统计一个小文件里面的ip的次数

 

 

2、寻找热门查询,1000万个查询字符串中统计最热门的10个查询(每个查询串的长度为1-255字节),要求使用的内存不能超过1G。

如果数据规模比较小,能一次性装入内存呢?比如这题,虽然有一千万个Query,但是由于重复度比较高,因此事实上只有300万的Query,每个Query255Byte,因此我们可以考虑把他们都放进内存中去(300万个字符串假设没有重复,都是最大长度,那么最多占用内存3M*1K/4=0.75G(约等于)。所以可以将所有字符串都存放在内存中进行处理),而现在只是需要一个合适的数据结构,在这里,HashTable绝对是我们优先的选择。

针对TOP K问题:

hash_map统计:先对这批海量数据预处理。具体方法是:维护一个Key为Query字串,Value为该Query出现次数的HashTable,即hash_map(Query,Value),每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可。最终我们在O(N)的时间复杂度内用Hash表完成了统计;
堆排序:第二步、借助堆这个数据结构,找出Top K,时间复杂度为N‘logK。即借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比。所以,我们最终的时间复杂度是:O(N) + N' * O(logK),(N为1000万,N’为300万)。

 

3、有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

解题思路:
1、遍历文件,对遍历到的每一个词,执行Hash操作:hash(x)%2000,将结果为i的词存放到文件ai中(这一步很关键,这样才能保证同样的单词被放到同一个小文件里面),通过这个分解步骤,可以是每个子文件的大小约为400KB左右,如果这个操作后的文件大小超过1MB,那么可以使用同样的方法把文件继续进行分解下去,直到文件的大小小于1MB为止。

2、统计出每个文件中出现频率最高的100个词。最简单的就是使用字典来实现(hashmap),具体方法为:遍历文件中的所有词,对于遍历到的词,如果字典中不存在,则把这个词存入到字典中(键为这个词,值为1),如果这个词已经在字典中,那么把这个词对应的值加一。遍历后可以非常容易的找到出现频率最高的100个词。

3、上一步找出了每个文件中出现频率最高的100个词,这步可以通过维护一个小顶堆来找出所有词中出现频率最高的100个词。遍历第一个文件,把第一个文件中的出现频率最高的100个词构成一个小顶堆。(如果第一个文件中词的数目小于100,那么可以继续遍历第二个文件,直到构建好有100个节点的小顶堆为止)。继续遍历,如果遍历到的词的出现次数大于堆顶上词的出现次数,那么可以用新遍历到的词替换堆顶的词,然后重新调整这个堆为小顶堆。当遍历完所有的文件后,这个小顶堆中的词就是出现频率最高的100个词。当然这一步也可以采用类似归并排序(对这两千个小根堆做归并排序,也可以采用数选择排序)的方法把所有文件中出现次数最高的100个词进行排序,最终找出出现次数最高的100个词。

时间复杂度分析:

1M = 1024 KB = 1024 * 1024 B 。然后1M / 16B = 2^16个单词=k,那么1G大概有多少个单词呢? 有2^26个=n单词,大概需要2^10次方个文件(我们选择2000个=m)

递归一遍O(n),每个小文件堆排序O(k*log(100)*m*logm*100)

 

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

  • hash映射:顺序读取10个文件,按照hash(query)%10(也是为了保证同一个字符串要被放到同一个文件里面)的结果将query写入到另外10个文件(记为a0,a1,..a9)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
  • hash_map统计:找一台内存在2G左右的机器,依次对用hash_map(query, query_count)来统计每个query出现的次数。注:hash_map(query,query_count)是用来统计每个query的出现次数,不是存储他们的值,出现一次,则count+1。
  • 堆/快速/归并排序:利用快速/堆/归并排序按照出现次数进行排序,将排序好的query和对应的query_cout输出到文件中,这样得到了10个排好序的文件(记为)。最后,对这10个文件进行归并排序(内排序与外排序相结合)

 

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

每个文件大小5G*64=320G,远远超过内存限制,所以不可能全部加载到内存中处理。

  • 分而治之/hash映射:遍历文件a,对每个url求取,然后根据所取得的值将url分别存储到1000个小文件(分别映射到某一个文件,可能文件名是a1,a2,a3......)。这样每个小文件的大约为300M。遍历文件b,采取和a相同的方式将url分别存储到1000小文件中(记为b1,b2,b3......)。这样处理后,所有可能相同的url都在对应的小文件(a1和b1,a2和b2)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
  • hash_set统计:求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

6.1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?

方案1:trie树或者hash_map都可以解决。

 

7.10万个字符串进行排序。

还是采用hash映射排好序然后归并。

或者用字典树解决这个问题也很好。

 

小tip:10亿一个G,100万一个M

 

方案二:bitmap位图法,可以节省内存空间.

1.在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数?

采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)。其实,这里可以使用两个普 通的Bitmap,即第一个Bitmap存储的是整数是否出现,如果再次出现,则在第二个Bitmap中设置即可。这样的话,就可以使用简单的1- Bitmap了。

2.给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?   

方案1:frome oo,用位图/Bitmap的方法,申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。

3.假设有这样一份数据,记录了全国1990-1999年出生的人的姓名和出生年月的键值对。假设正好有一千万人,那就要存储一千万个姓名和年份。如何运用Bitmap的思想来压缩数据呢?

有10个状态,就用10个一千万位的bit组。

 

 

海量数据的问题:

https://github.com/nonstriater/Learn-Algorithms/blob/master/Algorithms%20Job%20Interview/%E6%B5%B7%E9%87%8F%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86.md

额外拓展:

Tries树(字典树)

倒排索引

 
参考:
 
 
100亿个无序数里面找到中位数:
posted @ 2019-04-12 22:53  LeeJuly  阅读(174)  评论(0)    收藏  举报