目录
- 位图
- bitset介绍
- bitset使用
- 模拟实现
- 位图的应用——题目
- 总结
位图
bitset介绍
参考文档:bitset
在 C++ STL 中,std::bitset
是一个固定大小的容器类,用于表示二进制位序列。它可以被看作是一个长度固定为 N
的布尔数组,其中每个元素只有两种取值:0和1。
bitset使用
常见应用:1.快速查找某个数据是否在集合中 2.排序+去重 3.求两个集合的交集、并集等 4.操作系统中磁盘块标记
接口:
示例:
排序+去重
#include <iostream> // std::cout
#include <string> // std::string
#include <cstddef> // std::size_t
#include <bitset> // std::bitset
int main ()
{
std::bitset<5> foo (std::string("01011"));
std::cout << "foo contains:";
for (std::size_t i=0; i<foo.size(); ++i)
std::cout << foo.test(i) << ' ';
return 0;
}
模拟实现
代码:
template<size_t N>
class bitset
{
public:
bitset()
{
//开辟(N/8+1)个字节空间
_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位上的值
bool test(size_t x)
{
size_t i = x / 8;
size_t j = x % 8;
return _bits[i] & (1 << j);
}
//获取可以容纳的位的个数
size_t size()
{
return N;
}
private:
vector<char> _bits;
};
将一个整型数字x映射到位图中的具体的一个比特位。
- 找到在第几个字节里面(以char为单位开辟的空间)
- 找到在该字节的第几个位,并通过位操作(&、|、~)访问
以set()示例:
使用:
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;
}
位图的应用——题目
1.给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中
1 G = 1024 ∗ 1024 ∗ 1024 = 2 30 ≈ 10 亿 b y t e 1G = 1024*1024*1024=2^{30}\approx10亿byte 1G=1024∗1024∗1024=230≈10亿byte
40亿个整数大约需要占用16G空间大小
使用位图的话,用对应bit位的0/1来判断某个数字是否存在。
其中按照上述代码实现的bitset的话,template<size_t N>
,最多可以传
2
32
−
1
2^{32} - 1
232−1(4294967295,42亿多)大小的数字,这最大开辟42亿多比特位的空间,其中每个比特位都可以映射到对应的数。
bool judge()
{
bitset<-1> bs;
//bitset<0xffffffff> bs1;
//将整数映射到位图bs中
for()
{
bs.set(x);
}
//判断
bs.test(x);
}
2.给定100亿个整数,设计算法找到只出现一次的整数?
状态:0次、1次、2次及以上
思路:使用两个bitset,一个表示高位,一个表示第位。一个数就有{00, 01, 10, 11}4种表示了
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
bool inset1 = _bs1.test(x);
bool inset2 = _bs2.test(x);
// 00
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
_bs1.set(x);
_bs2.set(x);
}
}
//出现一次的整数: 01
void print_once_num()
{
for (size_t i = 0; i < N; ++i)
{
if (_bs1.test(i) == false && _bs2.test(i) == true)
{
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();
}
3.给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
同上题,使用两个bitset,一个bitset映射一个文件,映射位都是1的值就是交集
4.位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
同上上题,使用两个位图,一个高位,一个低位。
出现0次(00)、出现1次(01)、出现2次(10)、出现3次及以上(11)。找01和10
总结
位图的特点:
- 节省空间,快(直接定址法,不存在冲突)
- 相对局限,只能映射处理整型
🦀🦀观看~~