欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > 【数据结构与算法】位图 布隆过滤器 海量数据问题处理 哈希切分

【数据结构与算法】位图 布隆过滤器 海量数据问题处理 哈希切分

2025/5/2 9:00:19 来源:https://blog.csdn.net/lirendada/article/details/147652542  浏览:    关键词:【数据结构与算法】位图 布隆过滤器 海量数据问题处理 哈希切分

文章目录

  • Ⅰ. 位图
    • 一、问题引入
    • 二、位图概念
    • 三、位图的模拟实现
      • set 函数:将 x 位置处的比特位设为 1
      • reset 函数:将 x 位置处的比特位设为 0
      • test 函数:检查 x 位置处的比特位是否为 1
    • 四、位图的应用
    • 五、常见面试题
      • ① 给定100亿个整数,设计算法找到只出现一次的整数?
      • ② 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
      • ③ 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
  • Ⅱ. 布隆过滤器
    • 二、布隆过滤器的提出
    • 二、布隆过滤器的概念
    • 三、布隆过滤器的结构
    • 四、布隆过滤器的模拟实现
      • ① 插入函数 Set
      • ② 查找函数 Test
      • ③ 布隆过滤器的删除
    • 五、布隆过滤器的优点
    • 六、布隆过滤器的缺点
    • 七、布隆过滤器的应用场景
    • 八、布隆过滤器的面试题
  • Ⅲ. 布谷鸟过滤器
    • 一、布谷鸟过滤器由来
    • 二、布谷鸟哈希
    • 三、布谷鸟哈希的问题
    • 四、布谷鸟哈希的优化
    • 五、布谷鸟过滤器
    • 六、特殊的hash函数
  • Ⅳ. 哈希切分
      • 哈希切分的一道题
  • Ⅳ. 拓展阅读

在这里插入图片描述

Ⅰ. 位图

一、问题引入

​ 这里有道题:给 40 亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这 40 亿个数中。这里一共有三种方法:

  1. 遍历,时间复杂度 O(N)
  2. 先排序 O(NlogN),然后利用二分查找:O(logN)

​ 很明显发现前两种方法虽然可行,但是对于 40 亿个整数来说,内存开销是十分的大的,因为 40 亿个整数相当于是 16GB 左右,这个时候也更不可能去使用红黑树这些数据结构来解决,毕竟红黑树这些数据结构本身就带有一些负载的消耗(旋转),所以我们这里给出了第三种方法:位图!

二、位图概念

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

​ 比如上面的面试题,要我们开 40 亿个整数,并且判断一个数是否在这其中,那么我们只需要开辟 500MB 的空间大小,因为我们这里每个比特位代表一个数字存不存在的状态,比如 0 代表不存在,1 代表存在,那么一个比特位就足够啦,于是只需要开原本需要 16GB1/32 即可,也就是 500MB!这就很巧妙的解决了空间问题!

​ 而至于时间问题,查找某个数字的时候,位图也是通过某个数字的对应映射关系查找的,也就是通过哈希!所以时间复杂度为 O(1)

在这里插入图片描述

三、位图的模拟实现

​ 首先我们先把其框架搭出来:

// BitSet.h
#include <iostream>
#include <vector>
namespace liren
{class BitSet{private:std::vector<char> _bits; };
}

这里很奇怪哦,用到的是 vector 里面存的 char,为什么呢?

​ 仔细想一想,c++ 中最小的存储单位就是 char,由于我们要的是按比特位来识别数字,所以我们这里就用 char 类型来表示 8 个比特位,也就是 8 个数字,这样子也方便我们后面的位运算!(用 int 类型来存储也可以,但是一共有 32 个比特位,相比于 char 的话会更难控制一些,所以这里选用 char


​ ❓ 那么我们接下来的拷贝构造函数中如何来初始化呢?

这里我们用模板参数 N 来代表要初始化的比特位个数,那么问题又来了,数组开多大的空间呢❓

​ 🌛 由于我们数组中存放的类型是 char 类型,所以我们要得到 N 个比特位的话,则要 resize(N / 8),这里还有一个细节,就是还要多加一,因为假设 N10 的话,那么 N / 8 其实还是 1,那么我们开出来的就只有一个 char 也就是 8 个比特位,但是 N10 啊,所以为了保险起见,我们每次多开一个 char 的空间!也就是 resize(N / 8 + 1)

// BitSet.h
#include <iostream>
#include <vector>
namespace liren
{// N代表要存放的比特位数量template <size_t N>class BitSet{public:// 构造函数BitSet(){// 每次多开一个字节,并初始化为0_bits.resize(N / 8 + 1, 0);}private:std::vector<char> _bits; };
}

下面我们就来实现位图的几个主要的接口函数:(注意这里vs是小端,大端的实现略有不同

set 函数:将 x 位置处的比特位设为 1

​ 方法:先获取到 x 的位置,然后 按位或x 处比特位为 1 的数即可。

// 将x位置处的比特位设为1
void set(size_t x)
{// 先获取x的位置size_t i = x / 8;size_t j = x % 8;// 接着让x处的比特位(按位或)上(只有x处为1的数)// 注意这里vs是小端存储,注意方向_bits[i] |= (1 << j);
}

reset 函数:将 x 位置处的比特位设为 0

​ 方法:先获取到 x 的位置,然后让 x 处的比特位 按位与(&)上 x 处比特位为 0 其他位为 1 的数即可。

// 将x位置处的比特位设为0
void reset(size_t x)
{// 先获取x的位置size_t i = x / 8;size_t j = x % 8;// 接着让x处的比特位(按位与)上(只有x处为0的数)// 注意这里vs是小端存储,注意方向_bits[i] &= (~(1 << j));
}

test 函数:检查 x 位置处的比特位是否为 1

// 检查x位置处的比特位是否为1
bool test(size_t x)
{// 先获取x的位置size_t i = x / 8;size_t j = x % 8;// 接着让x处的比特位(按位与)上(只有x处为1的数)return _bits[i] & (1 << j);
}

​ 测试代码:

// test.cpp
#include "BitSet.h"int main()
{liren::BitSet<20> t;t.set(10);std::cout << t.test(10) << std::endl;t.reset(10);std::cout << t.test(10);return 0;
}

​ 接下来我们尝试着来解决上面遗留的面试题:

int main()
{// 40亿个整数,我们只需要开500MB的比特位即可// 但是为了保证int的范围都被包括,我们要开42亿整数空间的大小// 也就是unsigned的最大值// 但其实这里我们直接利用十六进制表示即可开unsigned的最大值//liren::BitSet<-1> b; 这种写法会报错liren::BitSet<0xffffffff> b;b.set(1314);std::cout << b.test(1314) << std::endl;b.reset(1314);std::cout <

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词