海量数据的面试题总结

1,给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址? 与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现

对于这个问题,最主要的就是要得到每个IP地址所出现的次数,只有得到了每个IP地址所出现的次数,我们就能得到出现次数最多的IP地址,也能够通过一个优先级队列(堆)从而找到前k个出现次数最多的IP地址,如何来得到每个IP地址的次数呢?
我首先想到的是通过遍历或者排序的方式来进行,但很明显这样的办法是很不好的,因为文件太大了,磁盘IO次数太多,所以只能换别的办法,下面这个办法可以解决这个问题
1,将100G的文件进行切割,切割成100个1G的文件,这样就将一个大问题从而简化成为一个小问题,但在切割的过程中要注意,我们并不是平均切割的,而是通过哈希的思想来进行切割。因为平均切割可能导致相同的IP放在不同的文件中,这样我们也没有办法来进行统计,我们只有把相同的IP地址放在相同的文件当中,才能够进行统计。
2,哈希切割-----将IP地址(可通过inet_addr(),将IP地址转化为一个长整形数据)%文件个数-----从而判断哪个IP进哪个文件中,这样就能保证相同的IP地址放在了同一个文件中,这样的思想类似于哈希桶。
3,针对每一个文件我们我们用unordered_map来进行统计,计算每一个IP所对应的次数。
4,通过优先级队列(小堆)来得到前K个出现次数最多的IP地址。

这种办法也有着一定的缺陷:
可能同一个文件中,相同的IP地址太多,大量的IP地址放在同一个文件当中,这是我们可以通过扩容的方式来进行解决。

2, 给定100亿个整数,设计算法找到只出现一次的整数?

首先想到的是通过哈希切割的方法来实现,将数据分散到不同的文件当中,再借助unordered_map来统计每一个数据出现的次数,最后返回只出现一次的数据即可,但这样的方法IO次数太多不可取。
最优办法:
将位图进行变形,位图原本只能一个比特位对于一个数据,现在使用两个比特位来表示数据的状态:

  • 00------表示数据不存在
  • 01------表示数据只出现一次
  • 10------表示数据出现多次

将数据向变形的位图中进行映射,计算数据对应的变形后位图中的两个比特位,然后设置这两个比特位的信息。如果这两个比特位里面是00,则表示这个里面没有数据,映射数据后变成01,如果这两个比特位里面是01,则表示这个里面已经存在该数据了,可以将它设置为10,如果已经是10了,则不需要处理。
在查找的时候,我们只需要计算数据得到它所映射的比特位,然后查看它是否为01,是01则表示该数据只出现了一次。

  1. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

将两个文件中的数据分别映射到两个位图中,将一个文件中的数据映射到位图中,需要512M的空间,两个文件映射到位图刚好需要1G的内存,映射完成后得到两个位图,将两个位图对应的字节进行&操作,就能取交集。

  1. 1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

和第二题思想类似,只需要在引入一个类型即可,用来表示出现两次的整数。

6,给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法

方法一:
将文件中的query先转化成整形数据再映射到位图中,然后用另一个文件中的query在位图中查找,如果找到所对应的比特位为1,则表示,该条query在另一个文件中出现了,否则没有出现。这种方式可能导致的误差比较大,因为可能有多条query对应一个比特位(像哈希冲突)可以通过一条query对应多个比特位来进行改进,如一条query对应5个比特位,这样误差就大大减小了。(通过布隆过滤器实现)
方法二:
将一个文件中的query映射到位图对应的比特位中,然后将query放入该比特位对应的文件当中(可以通过哈希的方式,对文件进行分割,然后用query进行取模找到相应的文件 )。查找的时候,另一个文件通过query在位图中的标记位,进行判断,如果为0,则表示该query一定不是交集,如果为1,则表示可能是交集,去该比特位对应的文件中去查找。

一,了解哈希结构

哈希表是一种快速的搜索结构,不需要经过任何比较,一次直接从表中得到要搜索的元素。 通过哈希函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系。

(1)插入元素
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
(2)搜索元素
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

二,哈希冲突:
原因:不同关键字通过相同哈希函数计算出相同的哈希地址。
解决方法:闭散列和开散列
(1)闭散列:当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。

  1. 线性探测

线性探测缺点:一旦发生哈希冲突,所有的冲突连在一起,容易产生数据“堆积”,即:不同关键码占据了可利用的空位置,使得寻找某关键码的位置需要许多次比较,导致搜索效率降低。

  1. 二次探测:最大的缺陷就是空间利用率比较低.

**什么时候扩容:**我们在使用线性探测解决哈希冲突的时候都不可避免的需要扩容,数据存的越多,就会有越来越多的冲突,当负载因子在0.7~0.8之间的时候,为了避免影响效率就需要进行扩容了。我们必须选一个合适的值来进行扩容,扩容也不能太频繁,因为哈希表的增容需要付出很大的代价,我们需要重新开辟一个新的空间,在把原来就哈希表中的数据经过重新计算他们的地址然后放入新的哈希表中去。

(2)开散列又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

两者比较:应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上: 由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。

猜你喜欢

转载自blog.csdn.net/weixin_44930562/article/details/104744948
今日推荐