海量数据查重问题一网打尽:哈希表、位图、布隆过滤器

目录

  • 海量数据查重问题一网打尽:哈希表、位图、布隆过滤器
  • 一、概述
  • 二、哈希表
  • 三、位图
  • 四、布隆过滤器
  • 总结

海量数据查重问题一网打尽:哈希表、位图、布隆过滤器

一、概述

在面试的时候可能经常会问道海量数据的查重问题,大概就是在有限的内存空间中进行数据的查重。在实际场景中,哈希表、位图、布隆过滤器都能发挥自己作用。下面概述一下三种数据结构的特点:

1. 哈希表使用的内存是最多的。在使用哈希的表过程中,需要一大块连续的内存空间。而且内存空间一般是存储元素的两倍。

2. 位图在某些情况下大大压缩了哈希表的使用空间。在使用的过程中,位图其实也需要开辟一段连续的内存空间,但是因为其数据底层用一个位来表示一个key的状态,极致的压榨了空间的利用率。但是一个位往往只能表示”是“、”否“这两个状态。其次,位图通常根据数据中的最大值来开辟数组的大小,因此可能在数据分布不均匀,存在个别大数的情况下造成空间的浪费。

3. 布隆过滤器解决了位图中”数据分布不均匀,存在个别大数而导致空间资源的浪费“这个问题。但是,对于布隆过滤器来说,它没办法删除一个key,也会存在数据的误判问题。

因此,在海量数据查重的时候,需要具体情况具体分析来选择合适的数据结构。

二、哈希表

在C++中的STL中,无序容器中的unordered_set、unordered_map、unordered_multiset、unordered_multimap就是使用哈希表实现的,具体的用法不再赘述。下面讨论的是哈希表在有限空间中的使用。

比如有个需求:两个文件中都有一亿条数据,每条数据为4个字节,现在只有10M的内存,如何找到两个文件中重复的数据?

如果直接把两个文件读到内存中的话,大概需要800M的内存空间(1亿约等于100M,100 * 4 * 2 = 800)。而只读取一个文件,另一个文件一条条依次读取的话也需要400M的内存空间,很明显不够用。因此,这里可以采用分治+哈希表的思想,把大文件分到多个小文件中,对比小文件是否存在重复来进行查重。

具体的做法是,可以用把每条数据先映射成一个数值,再用除留余数法把每条数据分配到一个和余数对应的小文件中。

比如,现在只有10M的内存,那就可以分到41个文件中(本来是40,取比40大的素数)。我们会对两个文件都进行同样的操作,那么,相同的数据就一定会放到相同下表的文件中。这样,我们可以把一个小文件的内容读取到内存中,并依次读取另一个文件的数据,往哈希表中查找。

注意,如果考虑到使用哈希表需要双倍的内存来避免哈希碰撞,因此,需要分成 80+个文件来执行以上的操作

三、位图

对于位图来说,和哈希表不同的最大地方体现在两处:

1. 使用一个位来表示一个数据存在与否,而哈希表可能用到了多个位(比如int就是32位)。
2. 开辟内存的大小需要用数据中的最大数来决定。

比如现在有三个数据 1, 2, 30

如果使用位图来存储的话,可以开辟32位(4个字节)的空间出来。比如,new char[4]。对于2来说就放在第0个char的第3个位置,而30就放在第3个char的第6个位置。相比于哈希表,需要4个int的位置(还需要双倍内存,4 * 3* 2 = 24个字节 )压缩了不少的内存。

但是,如果把30改成100000000,那就需要开辟比100000000多的位数来标记数字,因此存在一定的空间浪费。所以,布隆过滤器登场了。

四、布隆过滤器

布隆过滤器主要解决了前面的数据分布不均匀,存在个别大数而导致的内存空间浪费的问题。对于布隆过滤器来说,它也实现了一个位图用来作为标记。

那么,它是怎么怎么解决位图存在的问题呢?答案是用除留余数法,大数会变成一个小的余数,然后把小余数用一个位进行标记。

那出现哈希冲突怎么办呢?

对于布隆过滤器来说,对于一个数据,使用了多个映射函数把其转换为多个数值。对于多个数值同时取余,就可以得到多个余数,然后把余数对应在位图的位置都标记为1。

在查询的时候,需要求出多个余数。只有这多个余数对应位图的位置都是1,才说明这个数可能存在。但是如果有一个数对应在位图的位置为0的话,那就说明这个数据一定不存在。

那么,删除一个数据怎么办呢?答案是布隆过滤器没办法删除。因为一个位置可能被多个数据对应的余数重复标记了,是没办法把其中一个置零的。这也是布隆过滤器的一个缺点。

总结

由此可见,三种办法都有自己的应用场景。因此,在处理海量数据的时候,需要具体问题具体分析,选择合适的办法解决问题。

你可能感兴趣的:(数据结构,散列表,哈希算法,数据结构)