提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
在对C++的问题时,位图和布隆过滤器是可以解决实际问题的,当面对很多数据时,我们的服务器内存又很小时,我们需要进行拆分数据进行映射储存,然后进行查找,下面分享一些常见面试题和解决方案。
一、问题
哈希切割:
给一个超过100G大小的log file, log中存着IP地址,设计算法找到出现次数最多的IP地址?与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
位图的应用:
1.给定100亿个整数,设计算法找到只出现一次的整数?
2.给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
3.位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
布隆过滤器:
1.给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
2.如何扩展BloomFilter使得它支持删除元素的操作
二、问题解决
1.位图的应用
1.给定100亿个整数,设计算法找到只出现一次的整数?
(1)使用map/unordered_map进行储存的话,存在的问题是:数据太多,可能内存不够。所以还是需要位图进行变形解决;
(2)分析数据和问题:找只出现一次的数据->把数据分为三种,出现0次,1次,2次及以上。就是说每个值用两个位进行表示就可以,分别是00,01,10;
cpp
class solution
{
public:
void set(size_t x)
{
if(_bs1.test(x)==false&&_bs2.test(x)==false)//00
{
_bs1.reset(x);
_bs2.set(x);
}
else if(_bs1.test(x)==false&&_bs2.test(x)==true)//01
{
_bs1.set(x);
_bs2.reset(x);
}
}
private:
bitset __bs1;
bitset __bs2;
}
bitset是一个位图的类,我们用两个位图进行储存数据,当数据不存在时,映射一个数据就进行01映射,数据存在两次时,就不做处理就行了。这样就可以找到只出现一次的整数。
2.给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
(1)方案一:将其中一个文件1的整数映射到一个位图中,读取另外一个文件2中的整数,判断在不在位图中,在就是交集。消耗内存为500MB(因为只有42亿整数,存在重复)
(2)方案二:将文件1的整数映射到位图1中,将文件2的整数映射到位图2中,将两个位图的数进行按位与。与字后为1的位就是交集,消耗内存是方案一的两倍。
3.位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
思路和第一题差不多,还是用两个位表示,分为出现00,01,10,11次的,三次以上的用11表示即可。
cpp
class solution
{
public:
void set(size_t x)
{
if(_bs1.test(x)==false&&_bs2.test(x)==false)//00
{
_bs1.reset(x);
_bs2.set(x);
}
else if(_bs1.test(x)==false&&_bs2.test(x)==true)//01
{
_bs1.set(x);
_bs2.reset(x);
}
else if(_bs1.test(x)==true&&_bs2.test(x)==false)//10
{
_bs1.set(x);
_bs2.reset(x);
}
}
private:
bitset __bs1;
bitset __bs2;
}
出现三次之后就不用管了。就可以解决了,可以找到只出现两次的。
2.布隆过滤器
1.给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
(分析):query语句一般是SQL查询语句或者网络请求的url等,一般是一个字符串。100亿个query大约占300-600G,一个query大约30-60byte;
(1)方案一:将文件1中的query映射到一个布隆过滤器中,读取文件2中的query,判断在不在布隆过滤器中,在就是交集。
缺陷:交集中有些数据不准确,-》根据布隆过滤器的特点,可能存在误判,所以对存在的数据可能出现交集出现误判,-》但是交集中的数据不可能漏掉,因为布隆过滤器判断不存在是准确的。
方案一属于近似算法;-》可以先看看第二个问题再来看方案二的精确算法。
(2)方案二:两个文件都很大,大概在300-600G之间,也没有合适的数据结构能够精确的找出交集,所以我们可以将文件且割成多个小文件,小文件数据加载到内存中。
切的份数:一般切成可以放进内存中就行,这里一个文件300-600G,切成1000份,一个文件300-600MB,1G内存足够了,
若是平均切分,那么A0可以放到内存中存储到一个set中,B0-B999小文件中的数据都要跟A0比较,以此推,A1放到内存中,也要这样比较,,优势是可以放进内存中,但是需要不断地互相比较。
用哈希切分: 不是用平均切分了,而是使用哈希切分,i=hashstr(query)%1000;i是多少就进入第Ai/Bi个小文件中,两个文件都这样处理;所以下面比较只需要比较编号相同找交集就可以了;比较方法:将Ai放小文件的数据放到一个位图set中,读取对应的Bi小文件中query,看在不在Ai中,在就是交集。
2.如何扩展BloomFilter使得它支持删除元素的操作

因为一个位中映射是重复的,所以删除就会删除掉全部,导致数据丢失,所以原本的布隆过滤器不能进行删除操作,现在我们把每个位标记成计数器,
问题是: 到底要几个位来表示计数器,少了可能导致溢出,但是多了又消耗空间,,所以要注意计数器的位数大小,因为布隆过滤器的目的就是节省空间
3.哈希切割
1.给一个超过100G大小的log file, log中存着IP地址,设计算法找到出现次数最多的IP地址?与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
(问题分析):这里做的是统计次数,统计次数一般使用KV模型的map解决,但是问题是有100G的数据,放不到内存中。
所以我们这里需要进行哈希切割:先创建1000个小文件A0-A999,读取IP计算i=hashstr(IP)%1000;i是多少就进入Ai中,这样相同的IP就进入了同一个文件中。
然后我们对每个小文件进行读取;用map<string,int> countMap,读取Ai中的IP统计出次数,一个读完了clear,再读另一个,使用pair<string,int> max进行记录出现次数最多的IP就可以求出。
若是要找top K值,只需要一个堆就可以了,建造一个小堆,弹出顶元素就行,。。。。
总结
使用位图和布隆过滤器解决实际问题,同时哈希和布隆过滤器的结合使用。