【C++】位图模拟实现

文章目录

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

需要实现的接口

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个数

  • 如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<
    _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);//执行多少次,当前元素二进制位就有多少个1
            count ++;
        }
    }
    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++】位图模拟实现_第1张图片

所以就分为两步:

  • 数组的元素个数假设为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序列呢? ->取反为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");
}

bitset.h

#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;
	}
}

你可能感兴趣的:(C++,c++,开发语言)