C++必修:bitset的用法与实现

news2024/9/23 23:23:13

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++学习
贝蒂的主页:Betty’s blog

1. 位图的引入

首先我们来看一道面试题:

40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?

我们可能会提出以下思路:

  1. 遍历直接查找。
  2. 排序+二分查找。
  3. 利用红黑树或哈希表,即setunordered_set查找。

但是以上方法明显是错误的,因为对于40亿个整型来说有160亿byte,需要大概16G的内存空间( 1 G = 1024 M B = 1024 ∗ 1024 K B = 1024 ∗ 1024 ∗ 1024 b y t e ≈ 10 亿 b y t e 1G=1024MB=1024*1024KB=1024*1024*1024byte≈10亿byte 1G=1024MB=10241024KB=102410241024byte10亿byte)。我们不可能直接向内存这么大的空间,即使放在文件中每次处理一小部分效率也是极低的。为了解决这个问题,就要用到我们接下来要将的位图——bitset

2. 位图的概念

位图(bitset),就是用一个个比特位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。
然后解决上面这道问题,我们就可以利用二进制序列中的01代表某个无符号整数是否存在,其中无符号整数的最大值是 2 32 − 1 2^{32}-1 2321,即需要4294967295个比特位,大概512MB空间,这个空间大小就是我们可以接受的。

其中C++就为了我们提供了一个位图的模版类——位图

3. 位图的使用

3.1 位图的初始化

位图的初始化需要调用去构造函数,一般而言我们常用的就是以下几个接口
image.png

void Test1()
{
	//创建一个8位的位图,其所有位默认为0
	bitset<8> bit1;//000000000
	//创建一个16位的位图,其所有位设置为1
	bitset<16> bit2(0xffff);//1111111111111111
	//利用字符串初始化
	bitset<8> bit3(string("10010010"));//10010010
}

3.2 位图的成员函数

以下是位图的常见的成员函数,并且位图一般都重载了流插入<<以及流提取>>运算符。

成员函数功能
set设置指定位或所有位
reset清空指定位或所有位
flip反转指定位或所有位
test获取指定位的状态
count获取被设置位的个数
size获取可以容纳的位的个数
any如果有任何一个位被设置则返回 true
none如果没有位被设置则返回 true
all如果所有位都被设置则返回 true
[]返回对应位置的比特位数字
void Test2()
{
	bitset<8> bit;
	bit.set(2); //设置第2位
	cout << bit << endl; //00000100
	bit.flip(); //反转所有位
	cout << bit << endl; //11111011
	//被设置的个数
	cout << bit.count() << endl; 
	//获取指顶位的状态
	cout << bit.test(5) << endl;
	bit.reset(1); //清空第1位
	cout << bit << endl; //11111001
	bit.flip(2); //反转第2位
	cout << bit << endl; //11111101
	//一共多少比特位
	cout << bit.size() << endl; 
	//是否被设置
	cout << bit.any() << endl; 
	//清空所有位
	bit.reset(); 
	cout << bit.none() << endl; 
	//设置所有位
	bit.set(); 
	cout << bit.all() << endl; 
    for (int i = 0; i < 8; i++)
	{
		//获取指定位的状态
		cout << bit[i];
	}
	cout << endl;
}

image.png

3.3 位图的位操作

除此之外,位图还重载了大多数移位操作符方便我们使用
image.png

void Test3()
{
	bitset<8> bs1(string("10101010"));
	bitset<8> bs2(string("10101010"));
	bs1 >>= 1;
	cout << bs1 << endl; //01010101
	cout << (bs1 & bs2) << endl; //00000000
	cout << (bs1 | bs2) << endl; //11111111
	cout << (bs1 ^ bs2) << endl; //11111111
	bs2 |= bs1;
	cout << bs2 << endl; //11111111
}

image.png

4. 实现bitset

4.1 位图的结构

接下来我们来实现一下bitset的基本功能,首先bitset被定义为模版类,有一个非类型模版参数N,单位为比特位。然后成员变量我们可以利用一个整型数组来实现,一个整型有32个比特位,所以一般需要N/32+1个整型。

template<size_t N>
class bitset
{
public:
    //构造函数
    bitset();
    //设置位
    void set(size_t pos);
    //清空位
    void reset(size_t pos);
    //反转位
    void flip(size_t pos);
    //获取位的状态
    bool test(size_t pos);
    //获取可以容纳的位的个数
    size_t size();
    //获取被设置位的个数
    size_t count();
    //判断位图中是否有位被设置
    bool any();
    //判断位图中是否全部位都没有被设置
    bool none();
    //判断位图中是否全部位都被设置
    bool all();
private:
    vector<int> _bits; //位图
};

4.2 位图的初始化

位图初始化即通过构造函数将开辟的整型空间的比特位全部设为0,即整型设为0。

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

4.3 位图的位设置

位图我们可以先通过N%32计算修改的整型位置i,然后通过N%32得到修改的比特位的位置j。最后通过对应的位运算改变对应比特位的状态。
其中将对应比特位设置为1的运算为_bits[i] |= (1 << j)

//设置位
void set(size_t pos)
{
    assert(pos < N);
    int i = pos / 32;//第几个整型
    int j = pos % 32;//第几个比特位
    _bits[i] |= (1 << j);
}

其中将对应比特位设置为0的运算为_bits[i] &= ~(1 << j)

//清空位
void reset(size_t pos)
{
	assert(pos < N);
	int i = pos / 32;//第几个整型
	int j = pos % 32;//第几个比特位
	_bits[i] &= ~(1<< j);
}

其中将对应比特位翻转的运算为_bits[i] ^= (1 << j)

void flip(size_t pos)
{
    assert(pos < N);
    int i = pos / 32;//第几个整型
    int j = pos % 32;//第几个比特位
    _bits[i] ^= (1 << j);
}

其中将对应比特位的状态运算为_bits[i] & (1 << j)

//获取位的状态
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;
}

4.4 位图的其他操作

首先是获得位图的容量,直接返回对应的模版参数即可。

//获取可以容纳的位的个数
size_t size()
{
    return N;
}

接下来我们可以获取设置的位数,首先我们得知道num&num-1能将二进制最右侧的1去掉。

//获取被设置位的个数
size_t count()
{
	size_t cnt = 0;
	for (int i = 0; i < N / 32 + 1; i++)
	{
        //取每个整数
		int num = _bits[i];
		while (num)
		{
			num = num & (num - 1);
			++cnt;
		}
	}
	return cnt;
}

接下来就是判断位图中有没有位被设置,即判断每个整型是否为0,为0就没被设置,非0就已被设置。

//判断位图中是否有位被设置
bool any()
{
	for (int i = 0; i < N / 32 + 1; i++)
	{
		int num = _bits[i];
		if (num != 0)
		{
			return true;
		}
	}
	return false;
}

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

接下来我们判断是否全部位都被设置,我们可以先判断前N个是否设置如果全部被设置,那么其按位取反一定等于0,再取第N+1的每个比特位看是否为1

//判断位图中是否全部位都被设置
bool all()
{
    //前N个数
	for (int i = 0; i < N / 32 ; i++)
	{
		int num = ~_bits[i];
		if (num != 0)
		{
			return false;
		}
	}
    //第N+1个数
	for (size_t j = 0; j < N % 32; j++)
	{
		if ((_bits[N/32 - 1] & (1 << j)) == 0) 
			return false;
	}
	return true;
}

5. 经典面试题

5.1 问题一

给定100亿个整数,设计算法找到只出现一次的整数?

首先数据量达到100亿,肯定使用位图,然后我们分析可以将每个整数分为三种状态:没有出现过,出现过一次,出现两次及其上。这时我们不可能用一个位图解决,因为一个位图只能表示两个状态,所以我们可以用两个位图来表示。其中00表示没有出现过,01表示只出现过一次,10表示出现过两次及其以上:

template<size_t N>
class bitTwo
{
public:
	void set(size_t x)
	{
		//00->01
		if (!_bit1.test(x) && !_bit2.test(x))
		{
			_bit2.set(x);
		}
		//01->10
		else if(!_bit1.test(x) && _bit2.test(x))
		{
			_bit1.set(x);
			_bit2.reset(x);
		}
	}
	void PrintOnce()
	{
		for (int i = 0; i < N; i++)
		{
			//01
			if (_bit2.test(i) == true)
			{
				cout << i << " ";
			}
		}
		cout << endl;
	}
private:
	bitset<N> _bit1;
	bitset<N> _bit2;
};

5.2 问题二

给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件的交集?

我们提出以下两种解决方法:
方案一:

  1. 首先,依次读取第一个文件中的所有整数,将其映射到一个位图。这个位图需要有 2 32 2^{32} 232个比特位,即 512MB内存。
  2. 然后,读取第二个文件中的所有整数,逐个判断其是否在位图中。如果在,则说明该整数是两个文件的交集之一;如果不在,则不是交集。

方案二:

  1. 第一步,依次读取第一个文件中的所有整数,将其映射到位图 1。同样,位图 1 有 2 32 2^{32} 232个比特位,占用 512M内存。
  2. 第二步,依次读取第二个文件中的所有整数,将其映射到位图 2。位图 2 也占用512M内存,两个位图刚好满足1G内存的限制。
  3. 第三步,将位图 1 和位图 2 进行与操作,结果存储在位图 1 中。此时,位图 1 当中映射的整数就是两个文件的交集。

6. 位图源码

#include<vector>
#include<assert.h>
namespace betty
{
	template<size_t N>
	class bitset
	{
	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);
		}
		//获取位的状态
		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;
		}
		//获取可以容纳的位的个数
		size_t size()
		{
			return N;
		}
		//获取被设置位的个数
		size_t count()
		{
			size_t cnt = 0;
			for (int i = 0; i < N / 32 + 1; i++)
			{
				int num = _bits[i];
				while (num)
				{
					num = num & (num - 1);
					++cnt;
				}
			}
			return cnt;
		}
		//判断位图中是否有位被设置
		bool any()
		{
			for (int i = 0; i < N / 32 + 1; i++)
			{
				int num = _bits[i];
				if (num != 0)
				{
					return true;
				}
			}
			return false;
		}
		//判断位图中是否全部位都没有被设置
		bool none()
		{
			return !any();
		}
		//判断位图中是否全部位都被设置
		bool all()
		{
			for (int i = 0; i < N / 32 ; i++)
			{
				int num = ~_bits[i];
				if (num != 0)
				{
					return false;
				}
			}
			for (size_t j = 0; j < N % 32; j++)
			{
				if ((_bits[N/32 - 1] & (1 << j)) == 0) 
					return false;
			}
			return true;
		}
	private:
		vector<int> _bits; //位图
	};
}

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

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

相关文章

乌龙?揭露2024国自然被刷真相!

8月24日&#xff0c;国自然正式放榜&#xff0c;并且申请项目批准资助通知、不予资助通知以及专家评审意见也已发送到各位申请人手里。 中了的人兴奋庆祝&#xff0c;没中的人气愤懊恼&#xff0c;一遍又一遍的看着评审意见&#xff0c;甚至会质疑一些评审的意见有些 “不合理…

The Power of Scale for Parameter-Efficient Prompt Tuning

系列论文研读目录 文章目录 系列论文研读目录论文题目含义Abstract1 Introduction2 Prompt Tuning5.6.7.8.9.10. 论文链接 论文题目含义 刻度在参数高效快速调优中的作用 Abstract In this work, we explore “prompt tuning,” a simple yet effective mechanism for lear…

(四)Kafka离线安装 - Kafka下载及安装

Kafka官方下载地址&#xff1a;Apache Kafka 这时候下载安装版本。 我这里的安装目录在 /usr/local/ cd /usr/local/# 创建目录 mkdir kafka cd kafka mkdir kafka_log 把下载的压缩包&#xff0c;放入到/usr/local/kafka/目录下&#xff0c;解压。 # 解压 tar -zxvf kafka…

PDF招生简章如何转二维码?

​随着科技的不断发展&#xff0c;招生报名方式也在不断创新。如今&#xff0c;许多学校和企业都采用PDF招生简章来宣传招生。然而&#xff0c;传统的纸质招生简章存在携带不便、易损坏等问题。为了解决这些问题&#xff0c;将PDF招生简章转换为二维码成为了一种趋势。那你知道…

Laravel邮件发送功能的实现的方法和技巧?

Laravel邮件发信功能如何配置&#xff1f;怎么使用Laravel发信&#xff1f; 在现代Web开发中&#xff0c;邮件发送功能是不可或缺的一部分。Laravel框架以其优雅的语法和强大的功能&#xff0c;成为了许多开发者的首选。AokSend将深入探讨如何在Laravel中实现邮件发送功能&…

让甲方看得见服务器资源降本增效-软件开发不仅考虑开发成本也要重视长期的运维成本

这几天有几个开发者朋友问&#xff0c;用Go语言开发后端真能降低服务器成本吗&#xff1f;本文想分享是低成本一种解决方案&#xff0c;我们不讨论谁是世界上最好的开发语言&#xff0c;所以开发者朋友看到对比语言就不要去挣个高低。GoFly社区今天给大家分享我们这几年用下来真…

餐饮点餐外卖到店小程序系统管理

餐饮业主要以到店就餐和外卖方式/部分细分业快递配送、团餐等满足客户购餐消费需要&#xff0c;互联网时代&#xff0c;尤其是年轻人无论进店与否都追求快捷方便&#xff0c;商家也要提高自身服务效率。 制作餐饮外卖配送/到店/扫码点餐小程序并可在后台开启设置扫码点餐、到店…

黑神话悟空 PC端配置需求详解:如何为不同游戏体验选择合适的配置?

《黑神话&#xff1a;悟空》是一款备受期待的动作角色扮演游戏&#xff0c;由游戏科学&#xff08;Game Science&#xff09;开发&#xff0c;基于《西游记》改编。随着游戏的发布&#xff0c;许多玩家都在关心一件事&#xff1a;我的电脑能带动这款游戏吗&#xff1f;本文将详…

七年老玩家《王者荣耀》分析四:【更新与维护以及防沉迷系统】

目录 更新与维护 王者荣耀日常例行不停机维护的具体时间和内容是什么&#xff1f; 王者荣耀停机维护的历史案例及其对玩家体验的影响如何&#xff1f; 王者荣耀版本更新维护中&#xff0c;T0级英雄调整的标准和流程是什么&#xff1f; 王者荣耀大版本更新的准备工作包括哪些…

vscode链接到远程

点击左下角的绿色按钮选择远程链接成功后左下角会显示远程的名字点击打开文件夹&#xff0c;选择目录进行目录进行编辑

NSSCTF练习记录:[AFCTF 2018]BASE

题目&#xff1a; 根据题目&#xff0c;应为base家族解码&#xff0c;用工具套娃解就可以了

一、基于Vue3的开发-环境搭建【pnpm】安装

基于Vue3开发环境搭建 1、npm 的安装1.1、下载参考地址1.2、安装1.3、设置为国内镜像2、pnpm的安装2.1、启动PowerShell注意事项2.1、 安装2.2、常用命令3、创建项目1、npm 的安装 1.1、下载参考地址 //下载参考地址:https://nodejs.cn/download/#google_vignette下载界面 …

制造业如何利用MES管理系统实现数据采集

制造业利用MES&#xff08;制造执行系统&#xff09;管理系统实现数据采集是一个综合性的过程&#xff0c;它涉及多个环节和技术的协同工作。以下是一个详细的步骤说明&#xff1a; 一、设备接入与集成 设备连接&#xff1a;首先&#xff0c;需要将生产线上的各种设备&#xff…

css揭秘 7 结构与布局

36 自适应内部元素 众所周知&#xff0c;如果不给元素指定一个具体的 height&#xff0c;它就会自动适应其 内容的高度。假如我们希望 width 也具有类似的行为&#xff0c;该怎么做呢&#xff1f; <p>Some text [...]</p> <figure><img src"adamca…

苹果裁员与服务战略调整:科技巨头应对市场挑战的新动向

引言 近期&#xff0c;苹果公司的一系列动态引发了业界的广泛关注。从秋季发布会的定档到高层人事变动&#xff0c;再到服务部门的裁员决策&#xff0c;苹果正以前所未有的姿态调整其业务布局&#xff0c;以应对日益激烈的市场竞争和技术变革。 一、服务部门裁员&#xff1a;战…

Python+VScode 两个不同文件夹里的py文件相互调用|python的模块调用|绝对导入

第一次用VScode写python遇到了模块无法识别的问题&#xff0c;搞了一整天&#xff0c; 上网查&#xff0c;chatGPT都不行&#xff0c;现在时解决了。 首先项目结构如下&#xff0c;四个文件夹&#xff0c;四个py文件 代码&#xff1a; def f1fun():print("f1") de…

智能交通最新论文分享┆使用STG-Mamba进行时空图数据预测

论文简介 本推文主要介绍了一种基于选择性状态空间模型的时空图学习方法&#xff0c;论文标题为《STG-Mamba: Spatial-Temporal Graph Learning via Selective State Space Model》&#xff0c;第一作者为Lincan Li。时空图数据是一类广泛存在于日常生活中的非欧几里得数据&am…

双系统ubuntu引导项丢失如何修复

本来是win11和ubunt22.04的双系统&#xff0c;但是现在工作需要做一个外接固态里安装ubunt22.04去安装autoware, 按照装双系统的方法也装上了&#xff0c;引导项不知怎么回事&#xff0c;被设置在外接固态硬盘中了&#xff0c;导致开机必须插上外接固态才能进入引导项&#xff…

企业源代码加密软件有哪些?12款超好用的源代码加密软件推荐

在当今数字化的商业环境中&#xff0c;源代码作为企业核心竞争力的象征&#xff0c;保护其不被泄露和篡改显得尤为重要。源代码加密软件能够有效防止未经授权的访问、使用或分发&#xff0c;从而确保企业的知识产权和商业秘密不受侵害。本文将为您推荐12款超好用的源代码加密软…

基于yolov8的飞鸟检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的飞鸟检测系统是一个利用深度学习技术进行鸟类识别的应用。YOLOv8作为目前最先进的实时目标检测模型之一&#xff0c;以其高准确率和快速检测能力著称。下面简要介绍一个基于Python实现的飞鸟检测系统的核心要点。 首先&#xff0c;你需要安装必要的…