文章目录
1. 位图概念
- 所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。
1.1 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
- 注意:数据很大
- 搜素树和哈希表都不太行(内存存不下)
- 排序+二分查找(数据太大只能放在磁盘中,效率太低以及不好支持二叉查找)
- 位图–直接定址法(一个bit位映射标记值,1在;0不在)空间开512MB(半G)
- 遍历,时间复杂度O(N)
- 排序(O(NlogN)),利用二分查找: logN
- 位图解决:
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0
代表不存在。比如:
利用char通过除和模实现。
例子:如下图
2. 位图的实现
namespace Ding
{
template<size_t N>
class bitset
{
public:
bitset()
{
_bits.resize(N / 8 + 1, 0);
}
// 将x比特位置1
void set(size_t x)
{
size_t i = x / 8;
size_t j = x % 8;
_bits[i] |= (1 << j);
}
// 将x比特位0
void reset(size_t x)
{
size_t i = x / 8;
size_t j = x % 8;
_bits[i] &= ~(1 << j);
}
// 检测位图中x是否为1
bool test(size_t x)
{
size_t i = x / 8;
size_t j = x % 8;
return _bits[i] & (1 << j);
}
private:
vector<char> _bits;
};
void test_bit_set1()
{
bitset<100> bs1;
bs1.set(8);
bs1.set(9);
bs1.set(20);
cout << bs1.test(8) << endl;
cout << bs1.test(9) << endl;
cout << bs1.test(20) << endl;
bs1.reset(8);
bs1.reset(9);
bs1.reset(20);
cout << bs1.test(8) << endl;
cout << bs1.test(9) << endl;
cout << bs1.test(20) << endl;
}
}
2.1 运行结果:
3. 位图应用
-
- 给定100亿个整数,设计算法找到只出现一次的整数?
-
- 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
-
- 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数。
一个位图一般标记一个数在和不在;数据太大状态多;我们可以用两个位图来标记。
例如:00:未出现;01:表示出现一次;10:表示出现二次;11:表示出现3次及以上。两个位图分别取其中一个bit位来表示即可。
- 题目一:
- 题目二:
- 题目三:
3.1 具体代码封装实现如下
namespace Ding
{
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
bool inset1 = _bs1.test(x);
bool inset2 = _bs2.test(x);
if (inset1 == false && inset2 == false)
{
//->01
_bs2.set(x);
}
else if (inset1 == false && inset2 == true)
{
//->10
_bs1.set(x);
_bs2.reset(x);
}
else if (inset1 == true && inset2 == false)
{
//->11
_bs2.set(x);
}
}
void print_once_num()
{
for (size_t i = 0; i < N; ++i)
{
if (_bs1.test(i) == false && _bs2.test(i) == true) //01
{
cout << i << endl;
}
}
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};
void test_bit_set3()
{
int a[] = { 3, 4, 5, 2, 3, 4, 4, 4, 4, 12, 77, 65, 44, 4, 44, 99, 33, 33, 33, 6, 5, 34, 12 };
twobitset<100> bs;
for (auto e : a)
{
bs.set(e);
}
bs.print_once_num();
cout << endl << endl;
sort(a, a + sizeof(a) / sizeof(a[0]));
for (auto e : a)
{
cout << e << " ";
}
cout << endl;
}
}
3.2 部分结果演示:
3.3 应用总结:
- 快速查找某个数据是否在一个集合中
- 排序 + 去重
- 求两个集合的交集、并集等
- 操作系统中磁盘块标记
4. 位图特点
- 快,节省空间—直接定址法、不存在冲突
- 相对局限只能处理整形。