【C++】位图模拟实现

news2024/10/3 2:27:36

文章目录

    • 需要实现的接口
    • 构造函数
    • 如何得知要设置的元素的位置
    • 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<<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);//执行多少次,当前元素二进制位就有多少个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

image-20220603222937400

所以就分为两步:

  • 数组的元素个数假设为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<vector>
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<<j)进行与运算
			_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;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/436683.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Error:java: 程序包lombok不存在

Error&#xff1a;java: 程序包lombok不存在 有时候明明代码窗口里可以看到有这个类,但是启动就是报错说不存在(图1,图2),试过很多办法 1.查看工具中maven的设置,setting文件目录,maven目录,本地仓库目录 2.删除本地maven中的包,重新import, 3.jdk版本是否和pom里面的一致 4.重…

一文教你快速搭建数据驱动自动化测试框架

目录 1. 什么是数据驱动自动化测试框架 2. 搭建数据驱动自动化测试框架的步骤 步骤1&#xff1a;确定测试需求 步骤2&#xff1a;准备测试数据 步骤3&#xff1a;编写测试脚本 步骤4&#xff1a;选择测试工具 步骤5&#xff1a;搭建测试环境 步骤6&#xff1a;执行测试 …

二叉树相关基础选择填空题

目录 1、二叉树的( )遍历相当于广度优先遍历&#xff0c;( )遍历相当于深度优先遍历 2、已知某二叉树的前序遍历序列为5 7 4 9 6 2 1&#xff0c;中序遍历序列为4 7 5 6 9 1 2&#xff0c;则其后序遍历序列为&#xff08; &#xff09; 3、已知某二叉树的中序遍历序列为JGDHK…

信号平滑处理

信号平滑处理 此示例说明如何使用移动平均滤波器和重采样来隔离一天中时间的周期性分量对每小时温度读数的影响&#xff0c;以及如何去除开环电压测量中不需要的电线噪声。该示例还说明如何通过使用中位数滤波器对时钟信号的水平进行平滑处理&#xff0c;同时保留边沿。该示例…

c++算法初级8——递推

c算法初级8——递推 文章目录 c算法初级8——递推递推递推思想的运用错位排序杨辉三角&#xff08;二维递推&#xff09; 递推 递推思想&#xff1a; 根据已有的东西一点点地推出未知的东西。 使用递推解题三步骤&#xff1a; 数学建模找出递推式和初始条件写出代码。 张爽…

[STL]vector的使用+模拟实现

[STL]vector的使用模拟实现 文章目录 [STL]vector的使用模拟实现一、vector的使用1.构造函数2.迭代器3.容量操作4.vector的访问5.vector的修改 二、几个细节1.范围for2.扩容机制3.迭代器失效4.构造函数错误调用5.vector的深拷贝与浅拷贝6.vector的框架 三、vector模拟实现vecto…

hitcon_2017_ssrfme、[BJDCTF2020]Easy MD5、[极客大挑战 2019]BuyFlag

hitcon_2017_ssrfme 进入环境给出源码 <?php if (isset($_SERVER[HTTP_X_FORWARDED_FOR])) {$http_x_headers explode(,, $_SERVER[HTTP_X_FORWARDED_FOR]);$_SERVER[REMOTE_ADDR] $http_x_headers[0];}echo $_SERVER["REMOTE_ADDR"];$sandbox "sandbo…

Leetcode225. 用队列实现栈

文章目录 1.题目描述2.原题链接3.思路分析4.代码实现 1.题目描述 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void push(int…

树上差分(点差分/边差分)

树上差分一般有两种类型的题目&#xff0c;一种是对边进行差分&#xff0c;另一种就是对点进行差分。 对应的操作也有两种&#xff0c;对边进行差分的对应操作就是给定一对节点(u,v)&#xff0c;让我们把u到v之间路径上的边权都加val&#xff0c;对点进行差分的对应操作就是给…

经验正交分解EOF的Matlab的实现示例

在地学中&#xff0c;PCA和EOF通常用于信号提取&#xff0c;从繁杂的时空数据中分离出地理要素的时空变化特征&#xff0c;是进行地学信号分析的前提。本质上PCA和EOF没有什么不同&#xff0c;只是&#xff1a;EOF为空间特征向量&#xff0c;也称为空间模态&#xff0c;在一定程…

信号完整性分析:关于传输线的三十个问题解答(一)

1.什么是真正的传输线&#xff1f;&#xff08;What is a real transmission line?&#xff09; 答&#xff1a;真正的传输线由任意两条延长一定长度的导体组成。将一根导线标记为信号路径&#xff0c;将另一根导线标记为返回路径。 A real transmission line is composed o…

2023最经典的Python接口自动化测试中的用例编写问题总结

本篇文章分享几个接口自动化用例编写过程遇到的问题总结&#xff0c;希望能对初次探索接口自动化测试的小伙伴们解决问题上提供一小部分思路。 B站讲的最详细的Python接口自动化测试实战教程全集&#xff08;实战最新版&#xff09;_哔哩哔哩_bilibiliB站讲的最详细的Python接…

4月,不要跳槽...

跳槽是每个人都可能面临的选择&#xff0c;但不同的时间点会对跳槽带来不同的影响。对于软件测试人员来说&#xff0c;4月份并不是最适合的跳槽时间。原因如下&#xff1a; 与企业目标和计划相关。一般情况下&#xff0c;公司在1月份会制定本年度的发展目标和计划&#xff0c;而…

力扣sql中等篇练习(五)

力扣sql中等篇练习(五) 1 股票的资本收益 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 每个用户的所有Sell的price值减去Buy的price值就可以了 SELECT stock_name,SUM(IF(operationBuy,price*-1,price)) capital_gain_loss FROM Stocks GROUP B…

IT知识百科:什么是SSID?

一、什么是SSID SSID&#xff08;Service Set Identifier&#xff09;是无线网络中的一个重要概念&#xff0c;它是一个用于标识无线局域网&#xff08;WLAN&#xff09;的名称。SSID可以看作是无线网络的名称&#xff0c;类似于有线网络中的网络名称或者路由器的名称。在无线…

【JavaScript】5.JavaScript内置对象

JavaScript 内置对象 JavaScript 中的对象分为3种 自定义对象内置对象浏览器对象 前面两种对象是JS 基础 内容&#xff0c;属于 ECMAScript&#xff1b; 第三个浏览器对象属于JS 独有的 内置对象就是指 JS 语言自带的一些对象&#xff0c;这些对象供开发者使用&#xff0c;…

数据通信基础 - 数据通信方式

文章目录 1 概述2 分类2.1 按通信方向分2.2 按同步方式分 3 扩展3.1 网工软考真题 1 概述 分类维度分类解释举例通信方向单工通信信息 只能在一个方向发送&#xff0c;发送方不能接收&#xff0c;接收方不能发送电视、广播半双工通信通信双方可以 交替发送和接收信息&#xff…

分布式锁+AOP实现缓存

分布式锁AOP实现缓存 1、分布式锁AOP实现思想2、不使用AOP的情况2.1 没有使用缓存时代码2.2 使用Redis实现分布式锁的代码2.3 使用Redisson实现分布式锁2.4 测试缓存命中2.5 存在问题 3、分布式锁AOP实现3.1 定义注解3.2 定义一个切面类加上注解3.3 使用注解完成缓存 1、分布式…

函数的缺省参数,函数重载与底层函数名修饰解释,引用的初步介绍

TIPS 使用C输入输出更方便&#xff0c;不需要像printf/scanf输入输出时那样&#xff0c;需要手动控制格式。C的输入输出可以自动识别变量类型。在日常练习中&#xff0c;建议直接using namespace std即可&#xff0c;这样就很方便。using namespace std展开&#xff0c;标准库…

ReetrantLock源码剖析_03公平锁、非公平锁

一直努力就会有offer&#xff0c;一直努力就会有offer&#xff0c;一直努力就会有offer&#xff01; 文章目录 ReetrantLock公平锁代码解析ReetrantLock公平锁执行流程ReetrantLock非公平锁代码解析ReetrantLock非公平锁执行流程公平锁与非公平锁的比较 ReetrantLock公平锁代码…