Bitset
概述
位图主要适用于大量的数据处理,它强调的是利用比特位来标志对应数据的出现,是一种哈希形式。
类的实现
成员变量
由于数据需要映射到对应比特位,故仍需要开辟对应大小的空间,且必须支持随机访问,故采用 vector 实现。(deque 的随机访问比 vector 要慢,因为它实际上是通过链表 + 顺序表的方式实现的,故随机访问需要进行一系列计算和定位,故这里不采用 deque)而 vector 可以采用整型家族的任意类型实现,例如 char 是八个比特位,int 是 32 个比特位,只需要做好对应处理即可。在本文中将采用 char 进行叙述。
namespace Thepale
{
template <size_t N = 32>
class bitset
{
private:
std::vector<char> _v;
int _count;
};
}
这里采用了非类型模板参数,具体是希望用户在使用时可以直接指明需要储存的数据个数,而采用什么数据类型实现是不必指明的(例如使用 char 或 int)。由于同时需要检索已经使用的比特位个数,故添加一个 _count 计数。
成员函数
构造函数
构造函数需要完成空间的开辟,通常这一过程只需要调用 vector 的 resize 即可完成,需要注意的是除以对应数据的比特位后还需要加一,这是因为这里的空间并不能采用去尾法,而需要采用进一法,因为任何一个比特位都是有效的,不能随意舍弃。
bitset()
{
_v.resize(N / 8 + 1, 0);
}
由于本类中封装的是库中的 vector,不需要完成拷贝构造或析构等函数的实现。
test
此成员函数的功能是检查 pos 位置的比特位是 0 还是 1,这需要一定位运算技巧:
bool test(size_t pos)
{
assert(pos < N);
return _v[pos / 8] & (1 << (pos % 8));
}
首先找到该数据所在的 char,然后将 1 左移到对应位,可知这个左移的 1 除了一个 1 之外全部为 0,若 pos 所在的位为 0,则与后的结果为 0,若 pos 所在的位为 1,则与后结果不为 0,而逻辑值不为 0 即为 1。
set
set 需要将对应位设置为 1。故必然面临两种情况:1.当前位已经为 1;2.当前位为 0,需要设为 1。在已经为 1 的情况下,不需要更新 _count 计数器。
将 pos 位设置为 1 的方式即让 pos 位与对应的 1(其它位均为 0)相或,这样无论当前位是 0 还是 1 都可以将其变成 1 或维持 1 不变。
bool set(size_t pos)
{
if (pos >= N)
{
return false;
}
if(!test(pos))
{
_v[pos / 8] |= 1 << (pos % 8);
++_count;
}
return true;
}
reset
reset 的作用与 set 相反,需要将对应位设置为 0。同样,已经为 0 时不需要更新 _count 计数器。
将 pos 位设置为 0 的方式即让 pos 位与对应的 0(其它位均为 1)相与,这样无论 pos 位是 0 还是 1都可以将其变为 0 或维持 0 不变。
bool reset(size_t pos)
{
if (pos >= N)
{
return false;
}
if(test(pos))
{
_v[pos / 8] &= ~(1 << (pos % 8));
--_count;
}
return true;
}
count
返回有多少个比特位被 set 了。
int count()
{
return _count;
}
operator[]
下标引用操作符的作用和 test 一致,可以认为仅仅是对 test 的一层封装。
bool operator[](int pos)
{
return test(pos);
}
整体实现
#include <iostream>
#include <vector>
namespace Thepale
{
template <size_t N = 32>
class bitset
{
public:
bitset()
{
_v.resize(N / 8 + 1, 0);
}
bool test(size_t pos)
{
assert(pos < N);
return _v[pos / 8] & (1 << (pos % 8));
}
bool set(size_t pos)
{
if (pos >= N)
{
return false;
}
if (!test(pos))
{
_v[pos / 8] |= 1 << (pos % 8);
++_count;
}
return true;
}
bool reset(size_t pos)
{
if (pos >= N)
{
return false;
}
if (test(pos))
{
_v[pos / 8] &= ~(1 << (pos % 8));
--_count;
}
return true;
}
int count()
{
return _count;
}
bool operator[](int pos)
{
return test(pos);
}
private:
std::vector<char> _v;
int _count;
};
}