【数据结构】位图和布隆过滤器

一、位图

现在有一个问题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断

一个数是否在这40亿个数中?如果用int存储这40亿个数,太浪费内存了!我们可以考虑只用一

个比特位来标记一个数在不在(因为不重复)。下面的图很好的展示了位图的思想。

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用

来判断某个数据存不存在的。

1.位图的实现

位图的实现涉及到了左移和右移:结合注释并画图,你一定能明白代码的含义😁!

class bitset
{
public:
    //N:表示N个比特位(他不是精准的开N比特的空间,可能会比N略大)
    //但是N是你需要开的比特位
    bitset(size_t N)
    {
        _bits.resize((N/32)+1, 0);
        _num = 0;
    }
    
    //把x放进位图中 -> 就是把x映射到的比特位置成1
    void set(size_t x)
    {
        //1.计算x在哪个区间(x映射的位置在第几个整形)
        size_t index = x / 32;
        //2.计算x在这个整形的第几位
        size_t pos = x % 32;
        //把pos置成1
        _bits[index] |= (1 << pos);
        _num++;
    }
    
    //把x从位图中删除 -> 就是把x映射到的比特位置成0
    void reset(size_t x)
    {
        //1.计算x在哪个区间(x映射的位置在第几个整形)
        size_t index = x / 32;
        //2.计算x在这个整形的第几位
        size_t pos = x % 32;
        //把pos置成0
        _bits[index] &= ~(1 << pos);
        _num--;
    }
    
    //判断x在不在(也就是判断x映射到的位置是否为1)
    bool test(size_t x)
    {
        //1.计算x在哪个区间(x映射的位置在第几个整形)
        size_t index = x / 32;
        //2.计算x在这个整形的第几位
        size_t pos = x % 32;
        return _bits[index] & (1 << pos);
    }
    
private:
    std::vector<int> _bits;
    size_t _num;//表示数据个数
};

2.位图的优缺点

(1)位图的优点:节省空间,效率高。

(2)位图的缺点:只能处理整形,不能处理哈希冲突。

二、布隆过滤器

现在有这样的一个场景:我们在刷抖音时,它会给我们不停地推荐新的内容,所以它每次推荐时

要去重,去掉那些已经看过的内容。问题来了,抖音推荐系统如何实现推送去重的?记录已经给

你推荐过的内容,之后不再给你推荐已经推荐过的内容。表示一个内容已经推荐过,只需要标记

一个位就可以,特点是节省空间。这样会产生哈希冲突:不同的字符串映射到相同的位置上去

了,还没有推荐的内容被误判为已经推荐过了。如何解决上面讲的误判问题?不能解决误判的问

题,只能缓解,也就是降低误判的概率 -> 这就引出了布隆过滤器!

布隆过滤器提出的解决方案:一个值映射一个位置,容易误判,一个值映射多个位置可以降低误

判的概率 -> 这就是布隆过滤器!

下面这幅图很好的展示了一个值映射到多个位置(利用多个哈希函数映射到不同位置)

1.布隆过滤器的实现

布隆过滤器的大框架

//三个哈希函数
struct HashStr1
{
    //BKRD
    size_t operator()(const std::string& key)
    {
        size_t hash = 0;
        for(size_t i=0; i<key.size(); i++)
        {
            hash *= 131;
            hash += key[i];
        }
        return hash;
    }
};

struct HashStr2
{
    //RSHash
    size_t operator()(const std::string& key)
    {
        size_t hash = 0;
        size_t magic = 63689;
        for(size_t i=0; i<key.size(); i++)
        {
            hash *= magic;
            hash *= key[i];
            magic *= 378551;
        }
        return hash;
    }
};

struct HashStr3
{
    //SDBM
    size_t operator()(const std::string& key)
    {
        size_t hash = 0;
        for(size_t i=0; i<key.size(); i++)
        {
            hash *= 65599;
            hash += key[i];
        }
        return hash;
    }
};

//布隆过滤器
template<class K = std::string,
        class Hash1 = HashStr1,
        class Hash2 = HashStr2,
        class Hash3 = HashStr3>
class bloomfilter
{
public:
    bloomfilter(size_t num)
        :_bs(5 * num)//经过研究:布隆过滤器的长度是数据量的五倍最好
        ,_len(5 * num)//所以 位图的有效比特位 就是5*num
    {}
    
    void set(const K& key);//插入
    bool test(const K& key);//查找
    
private:
    My_bitset::bitset _bs;//位图
    size_t _len;//表示 位图中有效比特位的 长度
};

①这幅图很好的展示了布隆过滤器的结构和成员变量的含义(注意分成的两部分)

②三个哈希函数:实现多个映射关系。

(1)布隆过滤器的插入

下图展示了向布隆过滤器中插入:"baidu","tencent"

void set(const K& key)
{
    //解释为什么要 % _len
    //因为将字符串转化成的整数一般都非常大
    //所以要模上_len将 转化的整数 控制在 布隆过滤器的长度范围内
    size_t index1 = Hash1()(key) % _len;
    _bs.set(index1);
    
    size_t index2 = Hash2()(key) % _len;
    _bs.set(index2);
    
    size_t index3 = Hash3()(key) % _len;
    _bs.set(index3);
}
(2)布隆过滤器的查找
bool test(const K& key)
{
    size_t index1 = Hash1()(key) % _len;
    if(_bs.test(index1) == false)
        return false;
    
    size_t index2 = Hash2()(key) % _len;
    if(_bs.test(index2) == false)
        return false;
    
    size_t index3 = Hash3()(key) % _len;
    if(_bs.test(index3) == false)
        return false;
    
    //但是到这里也不一定是真的在,还可能是误判
    return true;
}

①判断不在,是准确的

②判断在,是不准确的:假如A在,那么一定能判断出A在;假如A不在,那么有可能误判成A在

(3)布隆过滤器的删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。

2.布隆过滤器的优缺点

(1)布隆过滤器的优点:节省空间,高效,可以标记存储任意类型。

(2)布隆过滤器的缺点:存在误判,不支持删除。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值