概念:位图本质上是个数组,用来存放数组,数组中的元素用来判断某个元素是否存在于这个位图集合中,当元素存在时,对应位的值为1;当元素不存在时,对应位的值为0
我们先判断一下下面的例子:
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
解决方法:
1.遍历
2.先排序,然后二分查找
3.位图解决
我们使用比特位来表示某个数是否存在,一个字节包含包含8个比特位,无符号整数的范围是0到-1,所以需要/8个字节,相比于其他方法非常节省空间
实现主要有三个接口:set、reset、test
template<size_t N>
class bitset
{
public:
bitset()
{
_bs.resize(N / 32 + 1);//一个整形是32个比特位
}
void set(size_t x)
{
int i = x / 32;//第几位
int j = x % 32;//对应位的比特位
_bs[i] = _bs[i] | (1 << j);
}
void reset(size_t x)
{
int i = x / 32;
int j = x % 32;
_bs[i] = _bs[i] & ~(1 << j);
}
//x映射位为1返回假,
//为0返回假
bool test(size_t x)
{
int i = x / 32;
int j = x % 32;
return _bs[i] & (1 << j);
}
private:
vector<int> _bs;
};
那如果给100亿个整数呢,设计算法找出只出现一次的数字,100亿个数字,肯定会有重复出现的数字,所以我们需要稍微修改一下位图,加一位来表示出现次数
这时候再添加一个位图即可,00表示0次,01表示1次,10表示2次及以上
代码如下:
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
if (!_bs1.test(x) && !_bs2.test(x))
{
_bs1.set(x);
}
else if (_bs1.test(x) && !_bs2.test(x))
{
_bs1.reset(x);
_bs2.set(x);
}
else if (_bs2.test(x) && !_bs1.test(x))
{
_bs1.set(x);
}
}
int get_count(size_t x)
{
if (!_bs1.test(x) && !_bs2.test(x))
{
return 0;
}
else if (_bs1.test(x) && !_bs2.test(x))
{
return 1;
}
else if (_bs2.test(x) && !_bs1.test(x))
{
return 2;
}
else
{
return 3;
}
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};