前言
大家好吖,欢迎来到 YY 滴 数据结构 系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁
主要内容含:
欢迎订阅 YY滴 数据结构 专栏!更多干货持续更新!以下是传送门!
目录
- 一.位图的基本概念
- 二.位图的原理
- 三.位图(bitset)的代码实现(逐过程解读)
- 【1】位图的文档查看
- 【2】把X映射的那个标记成1——对应biteset中的set
- 【3】把X映射的那个标记成0——对应biteset中的reset
- 【4】判断某位是1还是0——对应biteset中的test
- 【5】位图的完整实现
- 四.位图的经典应用场景
- 【※】对数据大小&转换的基本概念
- 【1】给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?
- 【2】给定100亿个整数,设计算法找到只出现一次的整数
- 【3】位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
- 【4】给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件的交集?
一.位图的基本概念
- 所谓位图,就是用 每一位 来存放某种状态,适用于海量数据,数据无重复的场景。
- 通常是用来判断某个数据存不存在的
二.位图的原理
- 哈希—— 直接定址法
- 例:
- 在实际场景中,我们的机器一般是 小端机(从左到右,从大到小排布)
- 所以真正的场景一般如下:
- 小端机性质 证明:
三.位图(bitset)的代码实现(逐过程解读)
【1】位图的文档查看
- 我们可以重点关注红圈圈出的三个位图常用函数
【2】把X映射的那个标记成1——对应biteset中的set
【3】把X映射的那个标记成0——对应biteset中的reset
【4】判断某位是1还是0——对应biteset中的test
【5】位图的完整实现
#include<vector>
namespace bit
{
template<size_t N>
class bitset
{
public:
bitset()
{
_a.resize(N / 32 + 1);//位图的初始化,确定分为多少块
}
// x映射的那个标记成1
void set(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
_a[i] |= (1 << j);
}
// x映射的那个标记成0
void reset(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
_a[i] &= (~(1 << j));
}
bool test(size_t x)
{
size_t i = x / 32;
size_t j = x % 32;
return _a[i] & (1 << j);
}
private:
vector<int> _a;
};
}
四.位图的经典应用场景
【※】对数据大小&转换的基本概念
- 1G =1024 MB=10241024 BK=10241024*1024 Byte= 2^30 byte = 10亿+ byte
- 例:我们判断40亿个整数需要多少G呢?
分析:40亿个int,160亿byte,根据10亿byte对应1G,160亿byte对应16G
【1】给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?
- 分析:常规思路是遍历/排序+二分查找
- 遍历的时间复杂度是O(N),排序(O(NlogN))+二分查找 logN
- 显然对于40亿无符号整数来说,需要占用16G,占用资源过于庞大,不妥
- 快速判断在不在,显然是位图经典场景,利用位图解决:
- 数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:
【2】给定100亿个整数,设计算法找到只出现一次的整数
- 分析:我们可以用两个位图来控制,我们可以这样设计
- 代码展示设计思路如图所示:
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
// 00 -> 01
if (!_bs1.test(x) && !_bs2.test(x))
{
_bs2.set(x);
} // 01 -> 10
else if (!_bs1.test(x) && _bs2.test(x))
{
_bs1.set(x);
_bs2.reset(x);
}
// 本身10代表出现2次及以上,就不变了
}
bool is_once(size_t x)
{
return !_bs1.test(x) && _bs2.test(x);
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};
【3】位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
- 此题的设计思路与上面的【2】基本一致,设计上要稍作改动:
- 代码展示设计思路如图所示:
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
// 00 -> 01
if (!_bs1.test(x) && !_bs2.test(x))
{
_bs2.set(x); //出现一次
} // 01 -> 10
else if (!_bs1.test(x) && _bs2.test(x))
{
_bs1.set(x);
_bs2.reset(x); //出现两次
}// 10 -> 11
else if (_bs1.test(x) && !_bs2.test(x))
{
_bs2.set(x); //出现三次
}
// 此外代表出现3次及以上,就不变了
}
bool max_two(size_t x)
{
return (_bs1.test(x) && !_bs2.test(x))||(!_bs1.test(x) && _bs2.test(x)); //10 或者 01
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};
【4】给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件的交集?
- 分析:
- 第一种思路是:把其中一个文件存入位图,遍历另一个文件元素,将问题转变成"在不在"问题
- 问题缺陷: 这种问题存在去重问题,即多次重复(下图中,交集明明只有一个3,但是会出现多个重复的3交集)
- 分析:
- 第二种思路是:将两个文件映射到两个位图中去(实现去重)
- 如果相对应的位置都是1(满足相&为1),则此元素就在交集中