文章目录
- 🦖 快速找出海量数据中是否存在该整型数据
- 🦖 有限内存情况下两个文件(海量query)中找出交集
- 🦖 海量数据中找出只出现1次的数据
- 🦖 有限内存情况下两个文件(整型)找出交集
- 🦖 海量数据中找出出现次数不超过2次的数据
- 🦖 海量数据中找出topK的IP
🦖 快速找出海量数据中是否存在该整型数据
在上篇文章『 C++ - STL 』位图(BitMap)与布隆过滤器(Bloom Filter)-CSDN博客中提到一题关于腾讯的面试题;
- 存在40亿个不重复的无符号整数,且无符号整数没排过序,给定一个无符号整数,如何快速判断一个数是否存在40亿数据当中;
这种题型为判断一个数据是否存在的问题,在C++
的STL
当中首先可以考虑set
,unordered_set
用于判断数据是否存在;
但是这题出现了另一个关键字 40亿 ;
-
这意味着数据不能直接存储至内存当中
40亿
个无符号整型在32位
的机器下大小约为16GB
;在一般的机器下无法能够完全将该数据量的数据存储至内存当中;
当然也可以使用以下思路:
-
排序+二分查找
先对数据进行排序,再使用二分查找的思路对数据进行甄别;
排序的时间复杂度最理想的情况其时间复杂度为
O(NlogN)
;二分查找的时间复杂度为
logN
; -
逐个数据进行遍历
使用逐个数据进行遍历的方式时间复杂度为
O(N)
;
尽管上述方式都能使其在40亿个数据中判断该数据是否存在,但整体效率过低;
在这种情况下即可以使用在『 C++ - STL 』位图(BitMap)与布隆过滤器(Bloom Filter)-CSDN博客 文章中所使用的位图;
以其使用位运算的方式将原数据大小为16GB
缩减至512MB
,且使用位图的时间复杂度基本上为常数级别O(1)
;
🦖 有限内存情况下两个文件(海量query)中找出交集
-
存在两个文件,分别有100亿个
query
,我们只有1GB
内存,如何找到两个文件交集?假设1个
query
的大小为50byte
,一个文件100亿个query
的大小则为5000亿byte
;5000亿byte
换算大小大约为500GB
;
在这种情况下可以使用 分割 的方式将文件 分割 为若干个小文件;
以当前情况为例,该题中限制内存大小为1GB
,则可以将文件 分割 为若干个约500MB
大小的小文件;
再将各个小文件放置于内存当中的set
或者unordered_set
容器当中判断其交集;
但是若是将文件进行平均切分,在遍历时只能采用暴力枚举的方式,对于检查的效率将会低至 O(N2) ,即每个文件都需要遍历另一组的所有小文件从而找出其中的交集;
-
而在这种情况为了避免该情况发生可以使用类似哈希桶的方式使大文件分割成小文件
遍历每个大文件,并使用一个哈希函数将文件中的各个
query
进行区分至各个小文件内;如:
i = HashFunc(query) % 1000
在使用该方式之后只需要将Ai
文件与Bi
文件放置不同的set
或unordered_set
并寻找交集即可;
在使用该方法对两个大文件进行处理的优势是,由于使用了哈希函数所以具有冲突或者完全相同的query
将会放置在对应下标的小文件当中,从而可以使得进行比较寻找交集,一般这种方式被称作 哈希切割 ;
但是使用这种方式也会出现对应的问题:
-
将数据放置在
set
或unordered_set
当中可能会出现 抛异常bad_alloc
这是因为采用 哈希切割 的切割方式,该方式并不是使得文件像上文所述的 平均切 方式从而导致了单个文件过大而不能完全加载进内存当中;
以该图为例(
A1
文件与B1
文件过大);
在这种文件过大的情况下通常有两种情况:
- 文件内存在大量不同且冲突的
query
; - 文件中存在大量相同的
query
;
若是出现文件内存在大量不相同且出现哈希冲突的query
时则换另一个哈希函数对该小文件重新进行 哈希切割 再次分成若干个小文件即可即可;
而文件中存在大量相同的query
时,由于set
或者unordered_set
容器具有去重的功能,所以在一般情况下文件中存在大量相同query
的情况时并不会抛出内存异常,只需要插入至对应的set
或者unordered_set
容器即可;
-
那么如何控制使得可以灵活进行 哈希切割 或是载入内存?
set
容器与unordered_set
容器在插入数据时,将会出现三种情况:-
插入成功
当数据插入成功时将会返回
pair
,其中pair
内存在对应的迭代器以及true
; -
插入失败
当数据存在时将会返回
pair
,其中pair
内存在对应的迭代器以及false
; -
插入失败(抛异常)
当内存不够时程序将抛出异常
bad_alloc
;
只需要灵活对异常进行处理则可以实现灵活进行 哈希切割 或是载入内存进行后续操作;
-
🦖 海量数据中找出只出现1次的数据
- 存在
100亿
个整数,找出只出现一次的整数;
该题为找出只出现一次的数据;
那么该思路可以使用 位图 进行解答;
因为数据量为100亿
,而整型的最大值为 232-1,故数据中必然存在大量的重复数值,而位图当中可以精确计算数据对应的是否存在问题,其最大数据为 232-1 ;
而位图中由于数据的存在表示为二进制0
或1
进行表示,故在此处只使用一个位图并不能很好的解决该问题;
而若是使用两个位图进行映射则可以表示4种情况,分别为00
,01
,10
,11
;
class bits{public:void set(const int n);void reset(const int n);private:std::bitset<-1> _bits1;std::bitset<-1> _bits2;
};
根据数据的情况以此对两个位图分别进行set
操作即可;
在该题中可以将数据分为以下情况:
-
数据不存在
当数据不存在时不需要进行
set
操作,其对应映射为00
; -
数据存在一个
当数据对应的映射为
00
时遍历到该数据时将其对应映射位置set
为01
,表示该数据出现一次; -
数据存在两个及以上
当数据对应的映射为
01
时再次遍历到相同数据将其对应映射位置set
为10
,表示数据出现过不止一次;当数据持续出现时且当前对应数据映射为
10
时保持不变(由于只需要判断只出现过 1次 的数据,不需要其他冗余操作);
最后检查位图对应的映射即可找出只出现一次的数据;
🦖 有限内存情况下两个文件(整型)找出交集
- 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
该题与上文中 有限内存情况下两个文件(海量query)中找出交集 题目类似,即在有限内存当中找出两个文件海量数据中的交集;
唯一不同的是该题的数据类型为整型;
由于整型的最大值为 232-1,而给定的文件的数据数据量分别有100亿
个,故两个文件中都必然存在大量的重复数值;
在这种情况中只需要使用位图即可以解决:
-
使用两个位图
定义两个位图,两个位图都对应着其中一个文件;
位图可进行映射与去重的操作,可将两个文件遍历并映射至位图当中进行去重;
最终比较两个位图找出其中对应的交集;
-
使用一个位图
定义一个位图,该位图对应两个文件中的其中一个文件;
遍历一个文件将该文件中的所有数据映射至位图当中;
再次遍历另外一个文件,当遇到相同值时保存该值(该数据为交集),并将该数据对应的映射置为
0
,即reset
操作;
两种方式对应的都有它的优缺点;
若是不需要频繁找其交集时可采用 方法2 寻找交集;
而若是需要频繁找数据的交集时则可以采用 方法1 从而降低每次访问文件的时间;
🦖 海量数据中找出出现次数不超过2次的数据
- 1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
该题与上文中 海量数据中找出只出现1次的数据 题目类似,且可以使用相同的模型进行解答;
由于在 海量数据中找出只出现1次的数据 题中所采用的模型为2个位图进行实现,其可以控制00
,01
,10
,11
四种情况;
在该题中且只需要该几种情况即可;
-
数据不存在
当该数据不存在时其对应两个位图的映射为
00
; -
数据出现1次
当该数据对应的映射为
00
,且遍历至该数据时,将该数据置为01
,表示数据出现1次; -
数据出现2次
当该数据对应的映射为
01
,且遍历至该数据时,将该数据置为10
,表示数据出现2次; -
数据出现3次及以上
当该数据对应的映射为
10
,且遍历至该数据时,将该数据置为11
,表示数据出现3次;当数据对应映射位置为
11
,且遍历至该数据时不采取措施,表示该数据出现3次或3次以上;
🦖 海量数据中找出topK的IP
- 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?与上题条件相同,如何找到top K的IP?
该题的解法与上文中 有限内存情况下两个文件(海量query)中找出交集 中的解决方式类似;
由于文件过大且类型为字符串类型,故不能采用位图进行操作;
再处理该题时则可以使用上文中出现的 哈希切割 的方式将大文件分成若干个小文件;
使用哈希函数遍历大文件中的IP
,并对其使用哈希函数并放置在对应的小文件当中;
将切分后的小文件采用map
或者unordered_map
统计其出现次数(相同或者冲突的IP
将会存放至同一个文件内);
在遍历小文件的过程当中可能会出现抛内存异常bad_alloc
;
当出现抛该异常时说明在遍历小文件的过程中某个文件中的数据量过大;
此时说明该小文件中冲突的数据(IP
)过多,导致的无法将小文件完全读取至内存当中;
此时只需要换一个哈希函数再次对该小文件进行 哈希切割 成更小的文件进行遍历即可;
并对其使用哈希函数并放置在对应的小文件当中;
将切分后的小文件采用map
或者unordered_map
统计其出现次数(相同或者冲突的IP
将会存放至同一个文件内);
在遍历小文件的过程当中可能会出现抛内存异常bad_alloc
;
当出现抛该异常时说明在遍历小文件的过程中某个文件中的数据量过大;
此时说明该小文件中冲突的数据(IP
)过多,导致的无法将小文件完全读取至内存当中;
此时只需要换一个哈希函数再次对该小文件进行 哈希切割 成更小的文件进行遍历即可;
而对于题目当中的对于Top K
问题则可以建一个小堆Heap
从而实现Top K
的问题;