> 文章列表 > 【C++】位图模拟实现

【C++】位图模拟实现

【C++】位图模拟实现

文章目录

    • 需要实现的接口
    • 构造函数
    • 如何得知要设置的元素位置
    • set
    • reset
    • flip
    • test
    • size
    • count
    • any
    • none
    • all
    • 打印位图的信息
  • bitset.h

需要实现的接口

namespace Mango
{template<size_t N>	//N表示开多少个比特位class bitset{public://构造函数bitset();//设置位,将某一个数对应的下标的所在比特位置为1void set(size_t pos);//清空位,将某一个数对应的下标的所在比特位置为0void reset(size_t pos);//反转位,将某一个数对应的下标的所在比特位置1->0 0->1void flip(size_t pos);//获取位的状态 读取某一个数对应的下标的比特位是1还是0bool 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个数

  • 如pos = 11, 11/8 = 1 11%8=3,所以应该把第1个char中的第3个比特位置为1

注意:比特位从右到左为低位到高位, 最低位为第0位


set

作用:设置元素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
}

reset

清空位,将某一个数对应的下标的所在比特位置为0

void reset(size_t pos)
{int i = pos / 8;//pos映射在容器的哪个下标位置int j = pos % 8;//pos在i位置元素的哪个比特位//其他位不变,只是将j位置置为0-> 和 ~(1<<j)进行与运算_bits[i] &= ~(1 << j);//将第i位置的元素的第j个比特位置为0
}

flip

反转位,将某一个数对应的下标的所在比特位置1->0 0->1

  • 如何反转某一位呢?该位异或上1即可
void flip(size_t pos)
{int i = pos / 8;//pos映射在容器的哪个下标位置int j = pos % 8;//pos在i位置元素的哪个比特位_bits[i] ^= (1 << j);//将第i位置的元素的第j个比特位反转 
}

test

获取位的状态 读取某一个数对应的下标的比特位是1还是0

bool test(size_t pos)
{int i = pos / 8;//pos映射在容器的哪个下标位置int j = pos % 8;//pos在i位置元素的哪个比特位return _bits[i] & (1 << j);
}

size

获取可以容纳的位的个数

size_t size()
{return N;//返回非类型模板参数的值
}

count

作用:获取位图中有多少个比特位设置为1,也就是统计位图中1的个数,我们只需要遍历每个char判断其中二进制中1的个数,然后累加起来即可

  • 统计二进制中1的个数,直接使用n&(n-1):将当前元素的最低位的1置为0,执行了多少次就说明当前元素比特位有多少1
//获取被设置位的个数 
size_t count()
{size_t count = 0;//求每个位置的二进制中1的个数for(auto n:_bits){while (n){n &= (n - 1);//执行多少次,当前元素二进制位就有多少个1count ++;}}return count;
}

any

判断位图中是否有位被设置过,只需要遍历每一个元素,如果所有元素都为0,说明没有位被设置过,如果出现某一个元素不为0,则说明有位被设置过

//判断位图中是否有位被设置
bool any()
{//如果有位被设置过,那么其二进制对应的值肯定不为0!for (auto n : _bits){if (n != 0)//该整数中有位被设置{return true;}}return false;//全部数都是0,则没有位被设置过
}

none

判断位图中是否全部位都没有被设置,我们只需要复用any函数即可,取反any函数的返回值

//判断位图中是否全部位都没有被设置
bool none()
{//复用any函数return !any();
}

all

判断位图中是否全部比特位都被设置,即判断是否每个元素都是全1序列

注意:最后一个元素并非是全部比特位都被使用!!!所以需要单独判断最后一个元素的前N%8

【C++】位图模拟实现

所以就分为两步:

  • 数组的元素个数假设为n,检查前n-1个char的二进制是否为全1序列
  • 检查最后一个元素的前N%8个比特位判断是否为全1
//判断位图中是否全部位都被设置 -> 每个元素的比特位都是全1序列
bool all()
{//先检查前n-1个数size_t n = _bits.size();for (size_t i = 0; i < n - 1; i++){//如果判断是全1序列呢? ->取反为0if (~_bits[i] != 0){return false;//说明不是全1序列}}//检查最后一个位置的元素的N%8位是否都为1for (size_t i = 0; i < N % 8; i++){//得到最后一个元素的第i个比特位,判断其是否为1if ((_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";elsecout << "0";}cout << endl;}//打印最后一个数的前N%8个比特位for (int i = 0; i < N % 8; i++){count++;if ((_bits[n - 1] & (1 << i)) != 0)cout << "1";elsecout << "0";}cout << endl;cout << "打印的位数为:" << count << endl;printf("------------打印结束----------------\\n");
}

bitset.h

#pragma once
#include<vector>
namespace Mango
{template<size_t N>	//N表示开多少个比特位class bitset{public://构造函数bitset(){//一个字符有8个比特位,要开辟N个比特位->需要开辟N/8+1个空间_bits.resize(N / 8 + 1, 0);//初始化为0}//设置位,将某一个数对应的下标的所在比特位置为1void set(size_t pos){int i = pos / 8;//pos映射在容器的哪个下标位置int j = pos % 8;//pos在i位置元素的哪个比特位_bits[i] |= (1 << j);//将第i位置的元素的第j个比特位置为1}//清空位,将某一个数对应的下标的所在比特位置为0void reset(size_t pos){int i = pos / 8;//pos映射在容器的哪个下标位置int j = pos % 8;//pos在i位置元素的哪个比特位//其他位不变,只是将j位置置为0-> 和 ~(1<<j)进行与运算_bits[i] &= ~(1 << j);//将第i位置的元素的第j个比特位置为0}//反转位,将某一个数对应的下标的所在比特位置1->0 0->1void flip(size_t pos){int i = pos / 8;//pos映射在容器的哪个下标位置int j = pos % 8;//pos在i位置元素的哪个比特位_bits[i] ^= (1 << j);//将第i位置的元素的第j个比特位反转 }//获取位的状态 读取某一个数对应的下标的比特位是1还是0bool 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序列呢? ->取反为0if (~_bits[i] != 0){return false;//说明不是全1序列}}//检查最后一个位置的元素的N%8位是否都为1for (size_t i = 0; i < N % 8; i++){//得到最后一个元素的第i个比特位,判断其是否为1if ((_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";elsecout << "0";}cout << endl;}//打印最后一个数的前N%8个比特位for (int i = 0; i < N % 8; i++){count++;if ((_bits[n - 1] & (1 << i)) != 0)cout << "1";elsecout << "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;//1bs.Print();bs.reset(5);cout << bs.test(5) << endl;//0bs.flip(5);cout << bs.test(5) << endl;//1bs.set(10);bs.set(20);bs.set(30);cout << bs.count() << endl;//4bitset<5> bs2;bs2.set(1);bs2.set(2);bs2.set(3);bs2.set(4);bs2.set(0);bs2.Print();cout << bs2.all() << endl;}
}