namespace Mango
{
template<size_t N> //N表示开多少个比特位
class bitset
{
public:
//构造函数
bitset();
//设置位,将某一个数对应的下标的所在比特位置为1
void set(size_t pos);
//清空位,将某一个数对应的下标的所在比特位置为0
void reset(size_t pos);
//反转位,将某一个数对应的下标的所在比特位置1->0 0->1
void flip(size_t pos);
//获取位的状态 读取某一个数对应的下标的比特位是1还是0
bool test(size_t pos);
//获取可以容纳的位的个数
size_t size();
//获取被设置位的个数
size_t count();
//判断位图中是否有位被设置
bool any();
//判断位图中是否全部位都没有被设置
bool none();
//判断位图中是否全部位都被设置
bool all();
//打印函数
void Print();
private:
vector<char> _bits; //底层开的是char空间
};
}
为了防止命令冲突,建议放在自己的命名空间
在构造的时候,需根据所给的位数N,创建一个N位的位图,并且将所有位都初始化为0
我们这里选择用char来表示,一个char由8个比特位,因此要构造N位的位图,需要N/8 +1
个char的空间, 因为N不一定是8的整数倍,
如:我们要构造10位的位图,需要如果只开辟10/8 = 1个char的空间,则会导致有两个元素没有被映射到
当然我们也可也选择用int来表示, 则需要用到N/32 +1
个整形的空间
注意:位图可能没有包含最后一个元素的所有比特位!!! 如上述构造10位的位图,开辟了两个char空间,就有6个比特位没有使用!!但是由于我们构造的时候,将所有的比特位都初始化为0了,所有不会对后续的判断使用产生影响
bitset()
{
_bits.resize(N/8 + 1,0);
}
我们要算出想要设置元素应该映射在哪个char的哪个比特位上
计算方式:在i个char的第j个比特位上 其中: i = pos/8 j = pos%8
每一个char代表8个数
注意:比特位从右到左为低位到高位, 最低位为第0位
作用:设置元素pos对应的比特位为1
//设置位,将某一个数对应的下标的所在比特位置为1
void set(size_t pos)
{
int i = pos / 8;//pos映射在容器的哪个下标位置
int j = pos % 8;//pos在i位置元素的哪个比特位
_bits[i] |= (1 << j);//将第i位置的元素的第j个比特位置为1
}
清空位,将某一个数对应的下标的所在比特位置为0
void reset(size_t pos)
{
int i = pos / 8;//pos映射在容器的哪个下标位置
int j = pos % 8;//pos在i位置元素的哪个比特位
//其他位不变,只是将j位置置为0-> 和 ~(1<
_bits[i] &= ~(1 << j);//将第i位置的元素的第j个比特位置为0
}
反转位,将某一个数对应的下标的所在比特位置1->0 0->1
void flip(size_t pos)
{
int i = pos / 8;//pos映射在容器的哪个下标位置
int j = pos % 8;//pos在i位置元素的哪个比特位
_bits[i] ^= (1 << j);//将第i位置的元素的第j个比特位反转
}
获取位的状态 读取某一个数对应的下标的比特位是1还是0
bool test(size_t pos)
{
int i = pos / 8;//pos映射在容器的哪个下标位置
int j = pos % 8;//pos在i位置元素的哪个比特位
return _bits[i] & (1 << j);
}
获取可以容纳的位的个数
size_t size()
{
return N;//返回非类型模板参数的值
}
作用:获取位图中有多少个比特位设置为1,也就是统计位图中1的个数,我们只需要遍历每个char判断其中二进制中1的个数,然后累加起来即可
n&(n-1):将当前元素的最低位的1置为0,执行了多少次就说明当前元素比特位有多少1
//获取被设置位的个数
size_t count()
{
size_t count = 0;
//求每个位置的二进制中1的个数
for(auto n:_bits)
{
while (n)
{
n &= (n - 1);//执行多少次,当前元素二进制位就有多少个1
count ++;
}
}
return count;
}
判断位图中是否有位被设置过,只需要遍历每一个元素,如果所有元素都为0,说明没有位被设置过,如果出现某一个元素不为0,则说明有位被设置过
//判断位图中是否有位被设置
bool any()
{
//如果有位被设置过,那么其二进制对应的值肯定不为0!
for (auto n : _bits)
{
if (n != 0)//该整数中有位被设置
{
return true;
}
}
return false;//全部数都是0,则没有位被设置过
}
判断位图中是否全部位都没有被设置,我们只需要复用any函数即可,取反any函数的返回值
//判断位图中是否全部位都没有被设置
bool none()
{
//复用any函数
return !any();
}
判断位图中是否全部比特位都被设置,即判断是否每个元素都是全1序列
注意:最后一个元素并非是全部比特位都被使用!!!所以需要单独判断最后一个元素的前N%8
位
所以就分为两步:
n-1
个char的二进制是否为全1序列//判断位图中是否全部位都被设置 -> 每个元素的比特位都是全1序列
bool all()
{
//先检查前n-1个数
size_t n = _bits.size();
for (size_t i = 0; i < n - 1; i++)
{
//如果判断是全1序列呢? ->取反为0
if (~_bits[i] != 0)
{
return false;//说明不是全1序列
}
}
//检查最后一个位置的元素的N%8位是否都为1
for (size_t i = 0; i < N % 8; i++)
{
//得到最后一个元素的第i个比特位,判断其是否为1
if ((_bits[n - 1] & (1 << i)) == 0)
{
return false;
}
}
return true;
}
为了验证我们上述函数的正确性,我们可以遍历位图,打印比特位信息
同时我们也可以顺带统计位图中为的个数,判断是否和我们传入的模板参数N相同,判断位图的大小是否符合我们的预期
注意:注意:最后一个元素并非是全部比特位都被使用!!!所以需要单独打印最后一个元素的前N%8
位
//打印函数
void Print()
{
printf("------------打印开始----------------\n");
int count = 0;//位的个数
size_t n = _bits.size();// 共开辟了n个空间
//先打印前n-1个数的二进制序列
for (int i = 0; i < n - 1; i++)
{
//我们这里存放的是char 有8个比特位
for (int j = 0; j < 8; j++)
{
count++;//位数++
if ((_bits[i] & (1 << j)) != 0)
cout << "1";
else
cout << "0";
}
cout << endl;
}
//打印最后一个数的前N%8个比特位
for (int i = 0; i < N % 8; i++)
{
count++;
if ((_bits[n - 1] & (1 << i)) != 0)
cout << "1";
else
cout << "0";
}
cout << endl;
cout << "打印的位数为:" << count << endl;
printf("------------打印结束----------------\n");
}
#pragma once
#include
namespace Mango
{
template<size_t N> //N表示开多少个比特位
class bitset
{
public:
//构造函数
bitset()
{
//一个字符有8个比特位,要开辟N个比特位->需要开辟N/8+1个空间
_bits.resize(N / 8 + 1, 0);//初始化为0
}
//设置位,将某一个数对应的下标的所在比特位置为1
void set(size_t pos)
{
int i = pos / 8;//pos映射在容器的哪个下标位置
int j = pos % 8;//pos在i位置元素的哪个比特位
_bits[i] |= (1 << j);//将第i位置的元素的第j个比特位置为1
}
//清空位,将某一个数对应的下标的所在比特位置为0
void reset(size_t pos)
{
int i = pos / 8;//pos映射在容器的哪个下标位置
int j = pos % 8;//pos在i位置元素的哪个比特位
//其他位不变,只是将j位置置为0-> 和 ~(1<
_bits[i] &= ~(1 << j);//将第i位置的元素的第j个比特位置为0
}
//反转位,将某一个数对应的下标的所在比特位置1->0 0->1
void flip(size_t pos)
{
int i = pos / 8;//pos映射在容器的哪个下标位置
int j = pos % 8;//pos在i位置元素的哪个比特位
_bits[i] ^= (1 << j);//将第i位置的元素的第j个比特位反转
}
//获取位的状态 读取某一个数对应的下标的比特位是1还是0
bool test(size_t pos)
{
int i = pos / 8;//pos映射在容器的哪个下标位置
int j = pos % 8;//pos在i位置元素的哪个比特位
return _bits[i] & (1 << j);
}
//获取可以容纳的位的个数
size_t size()
{
return N;
}
//获取被设置位的个数
size_t count()
{
size_t count = 0;
//求每个位置的二进制中1的个数
for(auto n:_bits)
{
while (n)
{
n &= (n - 1);
count ++;
}
}
return count;
}
//判断位图中是否有位被设置
bool any()
{
//如果有位被设置过,那么其二进制对应的值肯定不为0!
for (auto n : _bits)
{
if (n != 0)//该整数中有位被设置
{
return true;
}
}
return false;//全部数都是0,则没有位被设置过
}
//判断位图中是否全部位都没有被设置
bool none()
{
//复用any函数
return !any();
}
//判断位图中是否全部位都被设置 -> 每个元素的比特位都是全1序列
bool all()
{
//先检查前n-1个数
size_t n = _bits.size();
for (size_t i = 0; i < n - 1; i++)
{
//如果判断是全1序列呢? ->取反为0
if (~_bits[i] != 0)
{
return false;//说明不是全1序列
}
}
//检查最后一个位置的元素的N%8位是否都为1
for (size_t i = 0; i < N % 8; i++)
{
//得到最后一个元素的第i个比特位,判断其是否为1
if ((_bits[n - 1] & (1 << i)) == 0)
{
return false;
}
}
return true;
}
//打印函数
void Print()
{
printf("------------打印开始----------------\n");
int count = 0;//位的个数
size_t n = _bits.size();// 共开辟了n个空间
//先打印前n-1个数的二进制序列
for (int i = 0; i < n - 1; i++)
{
//我们这里存放的是char 有8个比特位
for (int j = 0; j < 8; j++)
{
count++;//位数++
if ((_bits[i] & (1 << j)) != 0)
cout << "1";
else
cout << "0";
}
cout << endl;
}
//打印最后一个数的前N%8个比特位
for (int i = 0; i < N % 8; i++)
{
count++;
if ((_bits[n - 1] & (1 << i)) != 0)
cout << "1";
else
cout << "0";
}
cout << endl;
cout << "打印的位数为:" << count << endl;
printf("------------打印结束----------------\n");
}
private:
vector<char> _bits; //底层开的是char空间
};
void TestBitSet()
{
bitset<100> bs;//存放100个比特位
bs.set(5);
cout << bs.test(5) << endl;//1
bs.Print();
bs.reset(5);
cout << bs.test(5) << endl;//0
bs.flip(5);
cout << bs.test(5) << endl;//1
bs.set(10);
bs.set(20);
bs.set(30);
cout << bs.count() << endl;//4
bitset<5> bs2;
bs2.set(1);
bs2.set(2);
bs2.set(3);
bs2.set(4);
bs2.set(0);
bs2.Print();
cout << bs2.all() << endl;
}
}