bitset(位图)的使用与模拟实现

news2024/12/25 9:34:33

bitset(位图)

    • 位图引入
    • bitset的使用
    • bitset(位图)的模拟实现
      • bitset类各函数接口总览
      • bitset类的实现
        • 构造函数
        • set、reset、flip、test
        • size、count
        • any、none、all
        • 打印函数

位图引入

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

要判断一个数是否在某一堆数中,我们可能会想到如下方法:

  • 将这一堆数进行排序,然后通过二分查找的方法判断该数是否在这一堆数中。
  • 将这一堆数插入到unordered_set容器中,然后调用find函数判断该数是否在这一堆数中。

单从方法上来看,这两种方法都是可以,而且效率也不错,第一种方法的时间复杂度是O ( N l o g N ) ,第二种方法的时间复杂度是O ( N )。

但问题是这里有40亿个数,若是我们要将这些数全部加载到内存当中,那么将会占用16G的空间,空间消耗是很大的。因此从空间消耗来看,上面这两种方法实际都是不可行的。

位图解决

实际在这个问题当中,我们只需要判断一个数在或是不在,即只有两种状态,那么我们可以用一个比特位来表示数据是否存在,如果比特位为1则表示存在,比特位为0则表示不存在。比如:
在这里插入图片描述
无符号整数总共有232个,因此记录这些数字就需要232个比特位,也就是512M的内存空间,内存消耗大大减少。

位图的概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

位图的应用

常见位图的应用如下:

  • 快速查找某个数据是否在一个集合中。
  • 排序。
  • 求两个集合的交集、并集等。
  • 操作系统中磁盘块标记。
  • 内核中信号标志位(信号屏蔽字和未决信号集)。

bitset的使用

bitset的定义方式

//方式一: 构造一个16位的位图,所有位都初始化为0。

bitset<16> bs1; //0000000000000000
//方式二: 构造一个16位的位图,根据所给值初始化位图的前n位。

bitset<16> bs2(0xfa5); //0000111110100101
//方式三: 构造一个16位的位图,根据字符串中的0/1序列初始化位图的前n位。

bitset<16> bs3(string("10111001")); //0000000010111001

bitset成员函数的使用

bitset中常用的成员函数如下:

成员函数功能
set设置指定位或所有位
reset清空指定位或所有位
flip反转指定位或所有位
test获取指定位的状态
count获取被设置位的个数
size获取可以容纳的位的个数
any如果有任何一个位被设置则返回true
none如果没有位被设置则返回true
all如果所有位都被设置则返回true

使用示例:

#include <iostream>
#include <bitset>//注意引入头文件
using namespace std;

int main()
{
	bitset<8> bs;
	bs.set(2); //设置第2位
	bs.set(4); //设置第4位
	cout << bs << endl; //00010100
	
	bs.flip(); //反转所有位
	cout << bs << endl; //11101011
	cout << bs.count() << endl; //6

	cout << bs.test(3) << endl; //1

	bs.reset(0); //清空第0位
	cout << bs << endl; //11101010

	bs.flip(7); //反转第7位
	cout << bs << endl; //01101010

	cout << bs.size() << endl; //8

	cout << bs.any() << endl; //1

	bs.reset(); //清空所有位
	cout << bs.none() << endl; //1

	bs.set(); //设置所有位
	cout << bs.all() << endl; //1
	return 0;
}

注意: 使用成员函数set、reset、flip时,若指定了某一位则操作该位,若未指定位则操作所有位。

bitset运算符的使用

1、bitset中>><<运算符的使用。

bitset容器对>>、<<运算符进行了重载,我们可以直接使用>>、<<运算符对biset容器定义出来的对象进行输入输出操作。

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

int main()
{
	bitset<8> bs;
	cin >> bs; //10110
	cout << bs << endl; //00010110
	return 0;
}

2、bitset中赋值运算符、关系运算符、复合赋值运算符、单目运算符的使用。

bitset容器中不仅对赋值运算符和一些关系运算符进行了重载,而且对一些复合赋值运算符和单目运算符也进行了重载,我们可以直接使用这些运算符对各个位图进行操作。

包括如下运算符:

  • 赋值运算符:=。
  • 关系运算符:==、!=。
  • 复合赋值运算符:&=、|=、^=、<<=、>>=。
  • 单目运算符:~。
#include <iostream>
#include <string>
#include <bitset>
using namespace std;

int main()
{
	bitset<8> bs1(string("10101010"));
	bitset<8> bs2(string("10101010"));
	bs1 >>= 1;
	cout << bs1 << endl; //01010101

	bs2 |= bs1;
	cout << bs2 << endl; //11111111
	return 0;
}

3、bitset中位运算符的使用。

bitset容器中同时也对三个位运算符进行了重载,我们可以直接使用&、|、^对各个位图进行操作。

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

int main()
{
	bitset<8> bs1(string("10101010"));
	bitset<8> bs2(string("01010101"));
	
	cout << (bs1&bs2) << endl; //00000000
	cout << (bs1|bs2) << endl; //11111111
	cout << (bs1^bs2) << endl; //11111111
	return 0;
}

4、bitset中[ ]运算符的使用。

bitset容器中对[ ]运算符进行了重载,我们可以直接使用[ ]对指定位进行访问或修改。

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

int main()
{
	bitset<8> bs(string("00110101"));
	cout << bs[0] << endl; //1
	bs[0] = 0;
	cout << bs << endl; //00110100
	return 0;
}

bitset(位图)的模拟实现

bitset类各函数接口总览

namespace cl
{
	//模拟实现位图
	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();
		//打印函数
		void Print();
	private:
		vector<int> _bits; //位图
	};
}

注:为了防止与标准库当中的bitset类产生命名冲突,模拟实现时需放在自己的命名空间当中。

bitset类的实现

构造函数

在构造位图时,我们需要根据所给位数N,创建一个N位的位图,并且将该位图中的所有位都初始化为0。

一个整型有32个比特位,因此N个位的位图就需要用到N/32个整型,但是实际我们所需的整型个数是N/32+1,因为所给非类型模板参数N的值可能并不是32的整数倍。

例如,当N为40时,我们需要用到两个整型,即40/32+1=2。

代码如下:

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

set、reset、flip、test

set

set成员函数用于设置位。

设置位图中指定的位的方法如下:

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

在这里插入图片描述
代码:

//设置位
void set(size_t pos)
{
	assert(pos < N);

	//算出pos映射的位在第i个整数的第j个位
	int i = pos / 32;
	int j = pos % 32;
	_bits[i] |= (1 << j); //将该位设置为1(不影响其他位)
}

reset

reset成员函数用于清空位。

清空位图中指定的位的方法如下:

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

在这里插入图片描述

代码:

//清空位
void reset(size_t pos)
{
	assert(pos < N);

	//算出pos映射的位在第i个整数的第j个位
	int i = pos / 32;
	int j = pos % 32;
	_bits[i] &= (~(1 << j)); //将该位设置为0(不影响其他位)
}
//注意: !-> 逻辑取反
//      ~ -> 按位取反

flip

flip成员函数用于反转位。

反转位图中指定的位的方法如下:

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

在这里插入图片描述

代码如下:

//反转位
void flip(size_t pos)
{
	assert(pos < N);

	//算出pos映射的位在第i个整数的第j个位
	int i = pos / 32;
	int j = pos % 32;
	_bits[i] ^= (1 << j); //将该进行反转(不影响其他位)
}

test

test成员函数用于获取位的状态。

获取位图中指定的位的状态的方法如下:

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

在这里插入图片描述

代码:

//获取位的状态
bool test(size_t pos)
{
	assert(pos < N);

	//算出pos映射的位在第i个整数的第j个位
	int i = pos / 32;
	int j = pos % 32;
	if (_bits[i] & (1 << j)) //该比特位被设置
		return true;
	else //该比特位未被设置
		return false;
}

size、count

size

size成员函数用于获取位图中可以容纳的位的个数。

我们直接将所给非类型模板参数进行返回即可。

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

count

count成员函数用于获取位图中被设置的位的个数。

获取位图中被设置的位的个数,也就是统计位图中1的个数,我们只需要依次统计每个整数二进制中1的个数,然后将其相加即可得到位图中1的个数。

统计二进制中1的个数的方法如下:

  • 将原数 n 与 n - 1 进行与运算得到新的 n 。
  • 判断 n 是否为0,若 n 不为0则继续进行第一步。

如此进行下去,直到 n 最终为0,此时该操作进行了几次就说明二进制中有多少个1,因为该操作每进行一次就会消去二进制中最右边的1,如下:

在这里插入图片描述

代码;

//获取被设置位的个数
size_t count()
{
	size_t count = 0;
	//将每个整数中1的个数累加起来
	for (auto e : _bits)
	{
		int num = e;
		//计算整数num中1的个数
		while (num)
		{
			num = num&(num - 1);
			count++;
		}
	}
	return count; //位图中1的个数,即被设置位的个数
}

any、none、all

any

any成员函数用于判断位图中是否有位被设置。

我们只需遍历每一个整数,若这些整数全部都为0,则说明位图中没有位被设置过。
虽然位图可能并没有包含最后一个整数的全部比特位,但由于我们构造位图时是将整数的全部比特位都初始化成了0,因此不会对此处判断造成影响。
在这里插入图片描述
代码:

//判断位图中是否有位被设置
bool any()
{
	//遍历每个整数
	for (auto e : _bits)
	{
		if (e != 0) //该整数中有位被设置
			return true;
	}
	return false; //全部整数都是0,则没有位被设置过
}

none

none成员函数用于判断位图中是否全部位都没有被设置。

位图中是否全部位都没有被设置,实际上就是位图中有位被设置的反面,因此none成员函数直接调用any成员函数,然后将返回值取反后再进行返回即可。

代码:

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

all

all成员函数用于判断位图中是否全部位都被设置。

判断过程分为两步:

  • 先检查前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;
}

打印函数

可以实现一个打印函数,便于检查我们上述代码的正确性,打印位图时遍历位图所包含的比特位进行打印即可,在打印位图的过程中可以顺便统计位图中位的个数count,将count与我们传入的非类型模板参数N进行比较,可以判断位图大小是否是符合我们的预期。

//打印函数
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)) //该位被设置
				cout << "1";
			else //该位未被设置
				cout << "0";
			count++;
		}
	}
	//再打印最后一个整数的前N%32位
	for (size_t j = 0; j < N % 32; j++)
	{
		if (_bits[n - 1] & (1 << j)) //该位被设置
			cout << "1";
		else //该位未被设置
			cout << "0";
		count++;
	}
	cout << " " << count << endl; //打印总共打印的位的个数
}

The end

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

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

相关文章

Cell Reports:任栓成/高东/胡志安/唐玲团队合作揭示压力性失眠发生的神经机制

良好的觉醒与睡眠是幸福生活和高效工作的前提。然而&#xff0c;随着现代社会生活节奏的加快&#xff0c;觉醒睡眠障碍&#xff0c;特别是失眠&#xff0c;已成为不容忽视的健康问题。据中国睡眠研究会发布的《2022中国国民健康睡眠白皮书》显示&#xff0c;约3/4人群曾存在觉醒…

Tpflow V7.0.2 PHP 工作流引擎新版发布

欢迎使用 Tpflow V7.0.1 工作流引擎 TpFlow 工作流引擎是一套规范化的流程管理系统&#xff0c;基于业务而驱动系统生命力的一套引擎。彻底释放整个信息管理系统的的活力&#xff0c;让系统更具可用性&#xff0c;智能应用型&#xff0c;便捷设计性。Tpflow 团队致力于打造中国…

如何提高企业运转效率?

企业运转效率是企业成功的重要因素之一&#xff0c;随着市场竞争的加剧和客户需求的不断提高&#xff0c;企业需要不断提高运转效率以保持竞争力。本文将从提高企业运转效率的意义、影响因素、优化方法等方面进行探讨。 提高企业运转效率的意义 提高企业运转效率的意义主要包…

提高Web应用程序稳定性的关键:如何编写高质量的Web自动化测试用例

目录 设计测试用例 编写测试用例 安装Selenium和Python库 编写测试用例 执行测试用例 创建测试套件 运行测试用例 分析测试结果 总结 Web自动化测试用例的编写是Web应用程序测试中非常重要的一环。它可以确保Web应用程序在不同条件和场景下的稳定性、可靠性和正确性。…

合合信息新推出反光消除技术,助力手写文字识别更精准

近期&#xff0c;合合信息旗下扫描全能王推出液晶手写板&#xff08;简称“手写板”&#xff09;&#xff0c;为用户带来仿真、流畅的书写绘画体验&#xff0c;一同发布的还有扫描全能王APP的新功能“拍手写板”。该功能可帮助用户在拍摄手写板内容后去除图片上的反光干扰&…

Tuxera NTFS2023第三方应用苹果电脑磁盘读写工具

哪里有专业级的NTFS格式读写工具&#xff1f;Tuxera NTFS2023中文版是一款非常好用的NTFS读写工具&#xff0c;可以让您完整的读写兼容NTFS格式驱动器&#xff0c;对磁盘进行访问、编辑、存储和传输文件等操作。同时还包括开源磁盘管理器等简单的格式和硬盘维修检查和修复。Mac…

即时通讯APP开发方案?你想要的全在这里!

现如今的即时通讯需求其实很多&#xff0c;应用场景也是多样化&#xff1b;企业内部办公、社交聊天、朋友闲聊都可以在即时通讯APP内完成。 即时通讯app开发对于企业来说是内部沟通的重要模式&#xff0c;对于普通用户来说这种交通方式也更加灵活化&#xff1b;即时通讯app开发…

酒店预订小程序开发:如何通过小程序提升您的酒店品牌价值?

在当今高度数字化的世界里&#xff0c;通过酒店小程序开发来提升品牌价值已成为一种越来越流行的方式。酒店小程序源码的开发可以帮助酒店提供更好的客户体验&#xff0c;提高预订转化率。在本文中&#xff0c;我们将探讨如何通过酒店小程序开发来提升您的酒店品牌价值。 酒店…

记录-因为写不出拖拽移动效果,我恶补了一下Dom中的各种距离

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 背景 最近在项目中要实现一个拖拽头像的移动效果&#xff0c;一直对JS Dom拖拽这一块不太熟悉&#xff0c;甚至在网上找一个示例&#xff0c;都看得云里雾里的&#xff0c;发现遇到最大的拦路虎就是JS…

GNSS/INS组合导航实习面试

GNSS/INS组合导航面试 美团无人机、云创智行、阿里达摩院、图森蔚来组合导航、来牟创新、 腾讯地图出行事业部、 持续更新 文章目录 GNSS/INS组合导航面试1.GNSS方面的问题模糊度固定的方法&#xff0c;以及部分模糊度固定多普勒和载波为什么精度更高&#xff0c;对多径更不敏…

你们工作中怎么用ChatGPT?如何高效Prompt?

当你还在错误使用对话 AI 工具如 GPT&#xff0c;可能会觉得其作用不过是知识平移总结或简单问答。实际上&#xff0c;当了解先进的用法、知悉如何做到 better prompt&#xff0c;你会发现&#xff1a;AI 不是来替代你的&#xff0c;是来帮助你更好工作。如果还用搜索引擎的“关…

【iOS-分类,拓展和关联对象底层探究】

前言 寒假分享会问题解决二 早在大一的OC的学习过程就知道了分类和拓展的区别和联系&#xff0c;分类不能添加成员变量&#xff0c;而拓展可以添加成员变量。分类是在运行时期实现的&#xff0c;而拓展只是编译器的时候就实现了。对于分类我们可以通过关联对象来为我们需要的分…

什么蓝牙耳机好?经销商分享线下热销蓝牙耳机品牌排行

开实体店铺多年&#xff0c;对数码行业多有研究&#xff0c;每天都会试用各种数码产品。网友们在发帖咨询什么蓝牙耳机好&#xff0c;为此我整理了实体店铺最受欢迎的蓝牙耳机品牌排行&#xff0c;大家在选购时可以作为备选&#xff1a; 第一款&#xff1a;JEET Air2蓝牙耳机 …

Mac M1/M2 安装nvm管理多版本node

Mac M1/M2 安装nvm管理多版本node Mac m1、Mac&#xff0c;Ventura 13.2&#xff0c;M2安装NVM使用homebrew安装nvm安装NodeMac nvm install failed python: not found解决方法(终端中执行以下命令)&#xff1a; Mac m1、Mac&#xff0c;Ventura 13.2&#xff0c;M2安装NVM 使…

2023最新XXL-JOB定时器教程

1.创建一个名为xxl_job的数据库,执行sql # # XXL-JOB v2.4.0 # Copyright (c) 2015-present, xuxueli.CREATE database if NOT EXISTS xxl_job default character set utf8mb4 collate utf8mb4_unicode_ci; use xxl_job;SET NAMES utf8mb4;CREATE TABLE xxl_job_info (id int(…

算法记录 | Day42 动态规划

01 背包 0-1 背包问题 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 **0-1 背包问题的特点&#xff1a;**每种物品有且仅有 1 件&…

28. Python logging日志模块下(适合小白)

28. Python logging日志模块下&#xff08;适合小白&#xff09; 文章目录 28. Python logging日志模块下&#xff08;适合小白&#xff09;1. %占位符格式化语法知识回顾2. basicConfig函数的参数3. format参数&#xff1a;设置输出的格式3.1 添加%(asctime)s字段输出日志发生…

电子商务网站上的API攻击如何泄漏PII

本稳重点分享&#xff1a; 以影子 API为目标的 API 攻击 电子商务网站上的 API 攻击如何泄漏 PII 对 API 运行时安全性重要性的看法 API 渗透测试指南 以影子 API为目标的 API 攻击 首先是DarkReading最近的一个研究的报告&#xff0c;该报告显示&#xff0c;大约50亿&am…

20+ Prompt工具网站汇总;我用AI工具开了一家「无人公司」;如何10分钟上线一个AI导航网站;第一部AIGC中英双语图文辞典 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 『MidJourney Prompt工具网站』加速生成与优化&#xff0c;持续更新中 ShowMeAI知识星球 | 资源标签&#xff1a;找工具 这是一个总结…

【unity专题篇】——GUI(IMGUI)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…