C++ bitset(位图)的模拟实现

news2024/9/29 15:01:22

文章目录

  • 一、bitset接口总览
  • 二、bitset模拟实现
    • 1. 构造函数
    • 2. set、reset、flip、test
    • 3. size、count
    • 4. any、none、all
    • 5. 打印函数
  • 三、完整代码


一、bitset接口总览

成员函数功能
set设置指定位或所有位为1(即设置为“已设置”状态)
reset清空指定位或所有位,将其设为0(即设置为“未设置”状态)
flip反转指定位或所有位的状态。如果位是0,则变为1;如果位是1,则变为0
test获取指定位的状态。如果位是1,则返回true;如果位是0,则返回false
count获取被设置为1的位的个数(即“已设置”的位的数量)
size获取位图可以容纳的位的总数。这通常指的是位图数组的总大小(以位为单位)
any如果有任何一个位被设置为1(即至少有一个位是“已设置”状态),则返回true;否则返回false
none如果没有位被设置为1(即所有位都是“未设置”状态),则返回true;否则返回false
all如果所有位都被设置为1(即所有位都是“已设置”状态),则返回true;否则返回false
#pragma once
#include <iostream>
#include <vector>
#include <assert.h>
namespace qi
{
	template <size_t N>
	class bitset
	{
	private:
		std::vector<int> _bits;
	public:
		bitset();
		void set(size_t pos);
		void reset(size_t pos);
		void flip(size_t pos);
		size_t size();
		size_t count();
		bool test(size_t pos);
		bool any();
		bool none();
		bool all();
		void Print(); //打印函数
	};
}

二、bitset模拟实现

1. 构造函数

在创建位图的时候我们需要根据所给的参数N,来构造一个N位的位图,并将该位图的所有位全部设置为0。

这里我们使用vector来做底层容器,一个int有32个比特位,但是非类型模板参数N,不一定总是32的整数倍,所以我们在构造的时候得先计算一下需要几个int。

例如,假如我们要建立一个50个比特位的位图,就需要两个int大小,共64个比特位,使用前50个比特位,后14个舍弃不用就好。

因此我们用式子 N/32+1来计算需要几个int。

在这里插入图片描述
具体实现也比较简单,直接调用vector的resize函数即可。

bitset()
{
	_bits.resize(N / 32 + 1, 0);
}

2. set、reset、flip、test

set,根据下标pos,把位图的该位置设置成1

  • 计算出该位位于第 i 个整数的第 j 个比特位。
  • 将1左移 j 位后与第 i 个整数进行或运算即可。

在这里插入图片描述

void set(size_t pos)
{
	assert(pos < N);
	int i = pos / 32;//第几个整数,算出来的i相当与下标
	int j = pos % 32;//第i个整数的第j个比特位,j也相当于下标
	//注意下标都是从0开始的
	_bits[i] |= (1 << j);
}

reset根据下标pos,将下标为pos位置的位图设置成0

  • 计算出该位位于第 i 个整数的第 j 个比特位。
  • 将1左移 j 位再整体反转后与第 i 个整数进行与运算即可。

在这里插入图片描述

void reset(size_t pos)
{
	assert(pos < N);

	int i = pos / 32;//第几个整数,算出来的i相当与下标
	int j = pos % 32;//第i个整数的第j个比特位,j也相当于下标
	//注意下标都是从0开始的
	_bits[i] &= (~(1 << j));//左移后要取反在&
}

flip根据下标pos反转指定下标的比特位

  • 计算出该位位于第 i 个整数的第 j 个比特位。
  • 将1左移 j 位后与第 i 个整数进行异或运算即可。

在这里插入图片描述


void flip(size_t pos)
{
	assert(pos < N);

	int i = pos / 32;//第几个整数,算出来的i相当与下标
	int j = pos % 32;//第i个整数的第j个比特位,j也相当于下标
	//注意下标都是从0开始的
	_bits[i] ^= (1 << j);
}

test检测下标pos处的位图是否为1,为1返回true,否则返回false

  • 计算出该位位于第 i 个整数的第 j 个比特位。
  • 将1左移 j 位后与第 i 个整数进行与运算得出结果。
  • 若结果非0,则该位被设置,否则该位未被设置。

在这里插入图片描述

bool test(size_t pos)
{
	assert(pos < N);

	int i = pos / 32;//第几个整数,算出来的i相当与下标
	int j = pos % 32;//第i个整数的第j个比特位,j也相当于下标
	//注意下标都是从0开始的
	if (_bits[i] & (1 << j))
	{
		return true;
	}
	return false;
}

3. size、count

size用于返回位图一共有多少位,也就是非类型模板参数N的值

直接返回N就好

size_t size()
{
	return N;
}

count用于计算该位图中有多少位是1,返回值是位图中1的个数
统计二进制中1的个数的方法如下:

  • 将原数 n 与 n - 1 进行与运算得到新的 n 。
  • 判断 n 是否为0,若 n 不为0则继续进行第一步。
  • 如此进行下去,直到 n 最终为0,此时该操作进行了几次就说明二进制中有多少个1。

因为该操作每进行一次就会消去二进制中最右边的1,如图:
在这里插入图片描述

size_t count()
{
	size_t count = 0;
	for (auto e : _bits)
	{
		int num = e;
		while (num)
		{
			num &= (num - 1);
			++count;
		}
	}
	return count;
}

4. any、none、all

any表示位图中只要有一个1那就返回true,否则返回false

判断方式比较简单,每一个整数的所有比特位,只要有一个为1,那该整数就肯定不等于0,所以,我们可以遍历所有整数,只要有一个整数不等于0,那就说明有1,返回true,否则所有整数都是0,没一个1,返回false

bool any()
{
	for (auto e : _bits)
	{
		if (e != 0)
		{
			return true;
		}
	}
	return false;;
}

none表示位图中如果全是0,没一个1那就返回true,否则返回false

其实和any是对立的,如果any为假,说明全为0,说明none为真,所以none中只需将any的结果取反后返回就好了。

bool none()
{
	return !any();
}

all表示,全为1返回true,否则返回false

判断过程分为两步:

  • 先检查前n-1个整数的二进制是否为全1。
  • 再检查最后一个整数的前N%32个比特位是否为全1。

需要注意的是,如果位图没有包含最后一个整数的全部比特位,那么最后一个整数的二进制无论如何都不会为全1,所以在判断最后一个整数时应该只判断位图所包含的比特位。

bool all()
{
	size_t n = _bits.size();
	//先检查前n-1个整数
	for (size_t i = 0; i < n - 1; i++)
	{
		if (~_bits[i] != 0) //取反后不为全0,说明取反前不为全1
			return false;
	}
	//再检查最后一个整数的前N%32位
	for (size_t j = 0; j < N % 32; j++)
	{
		if ((_bits[n - 1] & (1 << j)) == 0) //该位未被设置
			return false;
	}
	return true;
}

5. 打印函数

为了验证bitset对象的正确性,我们可以编写一个自定义的打印函数,该函数将遍历bitset中的每一位,并打印出其状态(0或1)。同时,这个函数还可以统计并返回bitset中实际被设置(即值为1)的位的数量,并将这个数量与bitset的模板参数(即预期的大小)进行比较,以确认bitset的大小是否符合预期。

#include <iostream>
#include <bitset>
using namespace std;

// 自定义打印函数,用于打印bitset并统计1的个数
template<size_t N>
void printAndVerifyBitset(const bitset<N>& bs) {
    size_t count = 0; // 用于统计1的个数
    cout << "Bitset: ";

```cpp
//打印函数
void Print()
{
	int count = 0;
	size_t n = _bits.size();
	//先打印前n-1个整数
	for (size_t i = 0; i < n - 1; i++)
	{
		for (size_t j = 0; j < 32; j++)
		{
			if (_bits[i] & (1 << j)) //该位被设置
				std::cout << "1";
			else //该位未被设置
				std::cout << "0";
			count++;
		}
	}
	//再打印最后一个整数的前N%32位
	for (size_t j = 0; j < N % 32; j++)
	{
		if (_bits[n - 1] & (1 << j)) //该位被设置
			std::cout << "1";
		else //该位未被设置
			std::cout << "0";
		count++;
	}
	std::cout << " " << count << std::endl; //打印总共打印的位的个数
}

三、完整代码

#pragma once
#include <iostream>
#include <vector>
#include <assert.h>
namespace qi
{
	template <size_t N>
	class bitset
	{
	private:
		std::vector<int> _bits;
	public:
		bitset()
		{
			_bits.resize(N / 32 + 1, 0);
		}
		void set(size_t pos)
		{
			assert(pos < N);
			int i = pos / 32;
			int j = pos % 32;
			_bits[i] |= (1 << j);
		}
		void reset(size_t pos)
		{
			assert(pos < N);

			int i = pos / 32;
			int j = pos % 32;
			_bits[i] &= (~(1 << j));
		}
		void flip(size_t pos)
		{
			assert(pos < N);

			int i = pos / 32;
			int j = pos % 32;
			_bits[i] ^= (1 << j);
		}
		size_t size()
		{
			return N;
		}
		size_t count()
		{
			size_t count = 0;
			for (auto e : _bits)
			{
				int num = e;
				while (num)
				{
					num &= (num - 1);
					++count;
				}
			}
			return count;
		}
		bool test(size_t pos)
		{
			assert(pos < N);

			int i = pos / 32;
			int j = pos % 32;
			if (_bits[i] & (1 << j))
			{
				return true;
			}
			return false;
		}
		bool any()
		{
			for (auto e : _bits)
			{
				if (e != 0)
				{
					return true;
				}
			}
			return false;;
		}
		bool none()
		{
			return !any();
		}
		bool all()
		{
			size_t n = _bits.size();

			for (int i = 0; i < n - 1; i++)
			{
				if (~_bits[i] != 0)
				{
					return false;
				}
			}

			for(int i = 0; i < N % 32; i++)
			{
				if ((_bits[n - 1] & (1 << i)) == 0) //该位未被设置
					return false;
			}
			return true;
		}
		//打印函数
		void Print()
		{
			int count = 0;
			size_t n = _bits.size();
			//先打印前n-1个整数
			for (size_t i = 0; i < n - 1; i++)
			{
				for (size_t j = 0; j < 32; j++)
				{
					if (_bits[i] & (1 << j)) //该位被设置
						std::cout << "1";
					else //该位未被设置
						std::cout << "0";
					count++;
				}
			}
			//再打印最后一个整数的前N%32位
			for (size_t j = 0; j < N % 32; j++)
			{
				if (_bits[n - 1] & (1 << j)) //该位被设置
					std::cout << "1";
				else //该位未被设置
					std::cout << "0";
				count++;
			}
			std::cout << " " << count << std::endl; //打印总共打印的位的个数
		}

	};
}

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

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

相关文章

华为eNSP:MAC地址安全

一、什么是MAC地址安全 MAC地址安全是一种网络安全措施&#xff0c;用于保护网络设备和通信免受未经授权的访问和潜在的安全威胁。以下是对MAC地址安全的详细介绍&#xff1a; MAC地址概述 定义&#xff1a;MAC地址&#xff08;Media Access Control Address&#xff09;是网络…

【绿豆蛙的归宿】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e510; const int M 2e510; int h[N], e[M], ne[M], idx, w[M]; double p[M]; int cnt[N]; double E; int n, m; void add(int a, int b, int c) // 添加一条边a->b {p[idx] 1, w[idx] c, e…

STM32F1+HAL库+FreeTOTS学习14——数值信号量

STM32F1HAL库FreeTOTS学习13——数值信号量 1. 数值信号量2. 相关API函数2.1 创建计数信号量2.2 获取信号量2.3 释放信号量2.4 删除信号量2.5 获取信号量的计数值 3. 操作实验1. 实验内容2. 代码实现&#xff1a;运行结果 上一期我们学习了二值信号量 &#xff0c;这一期学习计…

锅圈食品业绩承压显著:门店减少255家,押注肴肴领鲜打入农贸市场?

《港湾商业观察》廖紫雯 日前&#xff0c;锅圈食品&#xff08;上海&#xff09;股份有限公司&#xff08;以下简称&#xff1a;锅圈&#xff0c;02517.HK&#xff09;发布2024年上半年业绩运营情况。作为“在家吃饭第一股”&#xff0c;锅圈于2023年11月成功登陆港交所&#…

关于BSV区块链覆盖网络的常见问题解答(上篇)

​​发表时间&#xff1a;2024年9月20日 在BSV区块链上的覆盖网络服务为寻求可扩展、安全、高效交易处理解决方案的开发者和企业家开辟了新的视野。 作为开创性的曼达拉升级的一部分&#xff0c;覆盖网络服务提供了一个强大的框架&#xff0c;用于管理特定类型的交易和数据访问…

署名文章 | 对桂花AP2/ERFs的比较转录组分析揭示了OfERF017介导的有机酸代谢途径在花衰老中的作用

发表期刊&#xff1a;Frontiers in plant science 发表日期&#xff1a;2024年9月26日 影响因子&#xff1a;4.1 发表单位&#xff1a;湖北科技学院 研究背景 “玉露沾衣冷&#xff0c;金风拂面凉。桂花香满袖&#xff0c;秋色入诗囊。”桂花&#xff0c;中国十大名花之一…

Linux 网络配置 (深入理解)

前言 前期我比较迷惑Ubuntu 的网络配置。 我接触比较多的 Linux 发行版都是 Ubuntu &#xff0c;我按照网上的一些教程配置网络发现&#xff0c;没有相关网络配置文件夹。然后我发现不是我的问题而是不同版本的配置方式和工具是不一样的。然后有些配置已经弃用了。 常见的网络…

国庆节快乐|中国何以成为中国

华夏之土&#xff0c;广袤无垠&#xff1b;中华之史&#xff0c;源远流长。自古以来&#xff0c;中原大地物华天宝&#xff0c;人杰地灵&#xff0c;遂成一国&#xff0c;是谓中国。然中国之所以为中国&#xff0c;非徒地大物博、历史悠久也&#xff0c;更有其深厚之文化底蕴、…

实例讲解电动汽车冷却系统控制策略及Simulink建模方法

电动汽车的电机、电机控制器、DCDC、OBC在工作时都会大量发热&#xff0c;尤其是电机和电机控制器&#xff0c;功率大发热量大&#xff0c;且温度过高后会影响其正常功能&#xff0c;导致车辆故障无法正常行车&#xff0c;因此电动汽车冷却系统的控制也是一个非常重要的问题。本…

三维可视化技术的应用现状和发展前景

三维可视化技术的应用现状 工程建模 在工程领域&#xff0c;三维可视化技术被广泛应用于建筑设计、城市规划和工业制造等方面。通过三维建模软件&#xff0c;工程师可以创建逼真的模型&#xff0c;进行设计评估、碰撞检测和动态模拟&#xff0c;提高工程项目的效率与质量。 …

【傻呱呱】ESXI挂载USB移动硬盘给黑裙扩容

前期准备 ssh连接工具&#xff08;这里我用finalshell&#xff09; 删除移动硬盘分区&#xff08;此操作会删除硬盘内所有数据&#xff0c;注意备份&#xff01;&#xff01;&#xff01;&#xff09; 将需要挂载的usb移动硬盘连接到电脑上&#xff0c;使用分区工具&#xff…

1、Spring Boot 3.x 集成 Eureka Server/Client

一、前言 基于 Spring Boot 3.x 版本开发&#xff0c;因为 Spring Boot 3.x 暂时没有正式发布&#xff0c;所以很少有 Spring Boot 3.x 开发的项目&#xff0c;自己也很想了踩踩坑&#xff0c;看看 Spring Boot 3.x 与 2.x 有什么区别。自己与记录一下在 Spring Boot 3.x 过程…

安全类面试题-填空题

填空题 1、Linux下&#xff0c;复制/root/soure 到当前目录的命令是 cp -r /root/soure ./ 2、Linux下&#xff0c;解压缩bz2格式文件的命令是 bzip2 -d FileName 3、Linux下&#xff0c;查看网络连接状态的命令是 netstat 4、数据库触发器能监控的触发事件有&#xff1a; upda…

操作系统_名词_文件下载_反弹SHELL_防火墙绕过

操作系统 操作系统-用途&命令&权限&用户&防火墙 1、个人计算机&服务器用机 2、windows&Linux常见命令 3、文件权限&服务权限&用户权限 4、系统用户&用户组&服务用户等分类 5、自带防火墙出站&入站规则策略协议 实用案例1&#x…

如何在 Amazon EMR 中运行 Flink CDC Pipeline Connector

如何在Amazon EMR 中运行 Flink CDC Pipeline Connector 由于 Amazon EMR 最新的 Flink 版本中没有原生支持 Flink CDC&#xff0c;因此这里介绍一种通过 FlinkCDC Pipeline Connector 同步数据的例子(MySQL->Kafka) 环境准备 启动一个 Amazon EMR 集群 启动成功之后&#…

聚势启新 智向未来 | 重庆华阳通用科技有限公司揭牌成立

助推两江新区汽车产业高质量发展 (以下文字内容转载自两江新区网&#xff09; 9月26日&#xff0c;重庆华阳通用科技有限公司&#xff08;华阳通用重庆子公司&#xff09;在两江新区揭牌成立&#xff0c;将致力于智能座舱、智能驾驶两大领域&#xff0c;不断加大技术研发投入…

「系列投研|01」建立自己的移动比特币银行——赛道概况

ZJUBCA 09/28/2024 01&#xff5c;BTCfi赛道概况 BTCFi&#xff1a;建立自己的移动比特币银行 ——从Lending到Staking的全面解读 作者&#xff1a; Freya & Knight& Ausdin from ZJUBCA Elaine & Youyu from Satoshi Lab 关键词 BTCFi&#xff0c;稳定币&#xff…

DePIN 代表项目 CESS 受邀出席国会山活动,向议员展示创新 DePIN 技术

我们非常激动地宣布&#xff0c;CESS 已受邀参加由美国区块链协会主办的国会山活动&#xff0c;将于当地时间 2024 年 10 月 2 日向一众国会议员展示创新的 DePIN 技术&#xff01;本次关于去中心化物理基础设施网络&#xff08;DePIN&#xff09;的重要会议中&#xff0c;CESS…

windows下 Winobj.exe工具使用说明c++

1、winobj.exe工具下载地址 WinObj - Sysinternals | Microsoft Learn 2、接下来用winobj.exe查看全局互斥&#xff0c;先写一个小例子 #include <iostream> #include <stdlib.h> #include <tchar.h> #include <string> #include <windows.h>…

别再误用useMemo了!这才是最佳实践的正确打开方式

useMemo是react用作性能优化的一个hook&#xff0c;但有一个现象&#xff0c;不知道的人一次不用&#xff0c;知道的人随时随地到处都用。本文就带你真正搞懂什么情况下可以使用useMemo。 useMemo 是一个 React Hook&#xff0c;它在每次重新渲染的时候能够缓存计算的结果 useM…