C++实现位图

目录

一、什么是位图

二、位图类

1.参数及构造函数

2.set函数设置为1(代表存在) 

3.reset函数设置为0(代表不存在)

4.test函数查看状态(0还是1)

三、位图的变形


一、什么是位图

位图这个词汇比较少见,在学习位图之前,我们可以先来看看下面这个问题

40亿个数,大约为2^32。 这里有几个方案解决

解决方案1:暴力查找,将数据遍历一遍直到找到,但是40亿个数据太大了,如果换算为整数,那么至少需要16G(4*2^32代表4个字节,再乘以个数)的内存,一般的电脑是没办法开辟出这么大的连续空间的。

解决方案2:就是已经是有序了我们只需二分查找,二分查找的效率为log(n),但这里跟效率没啥关系,主要还是无法开辟这么大的内存的问题

解决方案3:将数据放到unordered_set或者set中,使用哈希表和红黑树的思想帮我们查找,但是哈希表和红黑树他们存放的结点会占用更多的空间,因为存放的结点不仅仅有数据,还有指针,因此更无法使用。

前面的几个解决方案都没有办法解决这种较大数据的问题,那么我们引入概念位图。

每一个值映射一个bit位,那么我们只需要一个bit位就可以知道该位置的值是否存在了,0为不存在,1为存在。这就叫做位图。

那么40亿个数据按照这种方法来表示他们是否存在的状态,只需要0.5G(2^32/8 代表所有的数据除以每个字节的8位)

二、位图类

其实库里面也有bitset位图,我们模仿一下这个位图的基本函数。

1.参数及构造函数

我们写出一个类,类的模板参数只有一个常量,代表我们要存放多少个数据的状态(bit位),类成员为vector<int> _bits; 

同时构造参数是给_bits开辟足够的空间,_bits.resize(N / 32 + 1, 0);   N/32代表一个整形的每一个bit位都存放状态,+1是为了防止除数把余数丢掉,比如40/32==1,如果我们只开辟一个int整形,就会将后面的数据丢掉。

template<size_t N>
class bitset
{
public:
	bitset()
	{
		_bits.resize(N / 32 + 1, 0);
	}
private:
    vector<int> _bits;
}

2.set函数设置为1(代表存在) 

 有了这个,我们如何来设置,让我们想要映射的位置变为1呢?

首先,参数部分肯定需要一个整数,代表是这个数据,我们可以通过 x/32 来表示映射到哪一个整形里了,再通过 x%32来表示映射该整形的哪一个bit位。这样我们就可以找到映射的位置了,找到位置后,应该如何将他的值赋为1呢?

我们的想法是让他无论是为0还是为1,我都要设置为1,这样初步会考虑到 |(或运算),肯定是 | 上1,我们可以通过左移运算符,将1左移到想要的位置,也就是 j 位置。这样进行或运算,就可以保证在不影响其他位置的值的前提,将想要的位置置为1了

代码如下 

void set(size_t x)
{
	int i = x / 32;
	int j = x % 32;
	_bits[i] |= (1 << j);
}

3.reset函数设置为0(代表不存在)

有了设置存在,我们也得设置不存在,如何操作呢?

一样的方法先找到 i 和 j ,无论该值为1还是为0,要想设置为0,就需要&(与运算)0才行那我们将 1 左移 j 位,再取反,就让想要的bit位变成了0,其他位都为1。

任何数(指0和1)与上1都是他本身,这样就没有改变其他bit位,任何数与上0都为0,这样就达到我们的设置改bit位为 0 目的了。

代码如下 

void reset(size_t x)
{
	int i = x / 32;
	int j = x % 32;
	_bits[i] &= ~(1 << j);
}

4.test函数查看状态(0还是1)

 有了置为1和置为0,我们还需要去查看该位置的状态。

依然是计算出  i  和  j  ,与上(1<<j)返回即可,因为任何数(指0和1)与上1都是他本身,因此我们状态为0就返回0,为1就返回1,就知道该位置的状态了。

bool test(size_t x)
{
	int i = x / 32;
	int j = x % 32;
	return _bits[i] & (1 << j);
}

测试一下,没有问题,这样就极大的节省了空间。

三、位图的变形

 如下面这个题,就是位图的变形题。

 这里也有两个方案去解决。

方案1:将单位图修改为双位图,从整形存放32个状态(0或1),编程存放16个(00或01或10)状态。由于找到只出现一次的整数,那么状态我们只需要设置(00或01或10)分别代表0个、1个、2个及以上。这个方案虽然可行,但是要重新修改位图,比较麻烦。

方案2:再写一个类,变量为两个位图,第一个位图代表状态(00或01或10)的前一个bit位,第二个位图代表状态(00或01或10)的后一个bit位

很明显,代码复用要香很多,我们选择方案2。

 set函数中,如果bit1的test(x)为false,bit2的test(x)也为false,那么就将bit2的该位置设置为1。

如果bit1的test(x)为false,bit2的test(x)为true,就将bit1位置设置为1,bit2位置设置为0,就完成了。

再写一个打印函数就完成了。

具体代码如下

template<size_t N>
class twobitset
{
public:
	void set(size_t x)
	{
		if (bit1.test(x) == false && bit2.test(x) == false)
		{
			bit2.set(x);
		}
		else if (bit1.test(x) == false && bit2.test(x) == true)
		{
			bit1.set(x);
			bit2.reset(x);
		}
	}
	void PrintOnce()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (bit1.test(i) == false && bit2.test(i) == true)
			{
				cout << i << endl;
			}
		}
		cout << endl;
	}
private:
	bitset<N> bit1;
	bitset<N> bit2;
};

代码测试,没问题。

该位图只涉及到了整数的处理,如果是字符串怎么处理呢?需要用到布隆过滤器,这个我们下次再讲。

最后附上总代码

bitset.h

#pragma once
#include<vector>
namespace kky
{
	template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_bits.resize(N / 32 + 1, 0);
		}
		void set(size_t x)
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] |= (1 << j);
		}

		void reset(size_t x)
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] &= ~(1 << j);
		}

		bool test(size_t x)
		{
			int i = x / 32;
			int j = x % 32;
			return _bits[i] & (1 << j);
		}

	private:
		vector<int> _bits;
	};
	template<size_t N>
	class twobitset
	{
	public:
		void set(size_t x)
		{
			if (bit1.test(x) == false && bit2.test(x) == false)
			{
				bit2.set(x);
			}
			else if (bit1.test(x) == false && bit2.test(x) == true)
			{
				bit1.set(x);
				bit2.reset(x);
			}
		}
		void PrintOnce()
		{
			for (size_t i = 0; i < N; i++)
			{
				if (bit1.test(i) == false && bit2.test(i) == true)
				{
					cout << i << endl;
				}
			}
			cout << endl;
		}
	private:
		bitset<N> bit1;
		bitset<N> bit2;
	};
}

 test.cpp

#include<iostream>
using namespace std;
#include"bitset.h"
//int main()
//{
//	kky::bitset<100> bs;
//	bs.set(10);
//	bs.set(11);
//	
//	cout << bs.test(9) << endl;
//	cout << bs.test(10) << endl;
//	cout << bs.test(11) << endl;
//	cout << bs.test(12) << endl;
//	bs.reset(10);
//	cout << bs.test(10) << endl;
//}




int main()
{
	int a[] = { 1,3,4,6,8,4,6,3,1,5,7 };
	kky::twobitset<100> bs;
	for (auto e : a)
	{
		bs.set(e);
	}
	bs.PrintOnce();
}

谢谢大家观看!!! 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

im6ull学习归纳总结(一)APP——04_文件IO

4.1文件从何而来 如图所示文件可以是 1真实文件保存在设备上 2内核提供的虚拟文件 3设备节点 4.2文件的访问方式 4.2.1通用IO模型&#xff1a;open/read/write/lseek/close 实验1 copy文件 代码 #include <sys/types.h> #include <sys/stat.h> #include <fc…

10 个顶级免费 Android 数据恢复软件可帮助恢复已删除的文件

不小心删除了手机上的一些重要数据或文件&#xff1f;这很不幸&#xff0c;但不要悲伤或放弃希望&#xff0c;因为仍有机会恢复它们。 10 个顶级免费 Android 数据恢复软件 虽然 Android 手机没有像 Windows 那样的回收站可以自动存储您删除的数据&#xff0c;但是有很多功能强…

大模型时代下的因果推断

导读&#xff1a;在数字化建设不断推进的今天&#xff0c;随着技术的不断发展&#xff0c;从统计学、机器学习、深度学习&#xff0c;再到因果学习&#xff0c;以及最新的热门大模型方向&#xff0c;九章云极DataCanvas始终紧贴最前沿的、最能助力企业和落地实践的方向&#xf…

合伙企业的优缺点是什么

合伙企业的优缺点是什么 一、合伙企业的优点 合伙企业在资本扩张方面较个人独资企业更有优势。个人独资企业仅有一个投资人&#xff0c;尽管存在整个家庭财产成为个人独资企业资本来源的情形&#xff0c;但该类企业资本规模相对较小、抗风险能力较弱。为扩张资本&#xff0c;单…

通过U盘:将电脑进行重装电脑

目录 一.老毛桃制作winPE镜像 1.制作准备 2.具体制作 下载老毛桃工具 插入U盘 选择制作模式 正式配置U盘 安装提醒 安装成功 具体操作 二.使用ultrasio制作U盘 1.具体思路 2.图片操作 三.硬盘安装系统 具体操作 示例图 ​编辑 一.老毛桃制作winPE镜像 1.制作准…

基本数据类型变量间的运算规则、基本数据类型与String的运算

目录 一、自动类型提升 二、强制类型转换 三、基本数据类型与String的运算 1 字符串类型&#xff1a;String 2 运算规则 在Java程序中&#xff0c;不同的基本数据类型&#xff08;只有7种&#xff0c;不包含boolean类型&#xff09;变量的值经常需要进行相互转换。转换的方…

产品原型设计软件 Axure RP 9 mac支持多人写作设计

axure rp 9 mac是一款产品原型设计软件&#xff0c;它可以让你在上面任意构建草图、框线图、流程图以及产品模型&#xff0c;还能够注释一些重要地方&#xff0c;axure rp汉化版可支持同时多人写作设计和版本管理控制&#xff0c;这款交互式原型设计工具可以帮助设计者制作出高…

playbook变量的使用(二)

接上一章&#xff1a; 内置变量 变量的过滤器 31.9 内置变量hostvars hostvars用来显示指定主机的 fact变量,用法如下。 1 hostvars[ 主机名 ].键值 此变量一般用于&#xff0c;当某个play的 hosts 中只写了A主机组&#xff0c;但是同时想在此play中显示B 主机组中的信息,这…

Gradle中 Implementation 与API 声明依赖方式的对比

在Gradle中&#xff0c;implementation和api是声明依赖的两种方式&#xff0c;它们在如何暴露依赖关系方面有所不同&#xff1a; Implementation: 当一个模块使用implementation声明依赖时&#xff0c;该依赖仅对声明它的模块可见。这意味着该依赖对于该模块的消费者是隐藏的。…

回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 (多指标,多图)

回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 &#xff08;多指标&#xff0c;多图&#…

如何通过ssh管道传输文件到ubuntu

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 如何在window系统中&#xff0c;通过ssh将指定的文件传输到ubuntu中呢&#xff1f; 比较常用的有以下种方式&#xff1a; 共享文件夹借助工具&#xff0c; FileZillaMobaxtermWinSCPXshell XFTP samba互传PuTTY pscp 今天主要…

【Mode Management】CanSM详细介绍

1. Introduction and functional overview AUTOSAR BSW栈为每个通信总线指定一个总线特定的状态管理器。CANSM实现CAN总线的数据流控制。CanSM隶属于通信服务层。CanSM和通信硬件抽象层以及系统服务层交互。 CanSM只用用于控制CAN通信。CanSM的任务就是操作CanIf模块去控制一个…

Python中abstractmethod的使用教程

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Python中&#xff0c;抽象类和抽象方法提供了一种强制子类实现特定方法的机制。abstractmethod是abc&#xff08;Abstract Base Classes&#xff09;模块中的一部分&#xff0c;它允许定义抽象方法&#xff0c…

阿赵UE学习笔记——3、常用界面窗口

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。继续学习虚幻引擎&#xff0c;这次介绍的是编辑器的常用界面窗口。 一、视口 这个视口的概念&#xff0c;可以体现出UE对于多屏幕同时显示是多么的友好。我们开发游戏的时候&#xff0c;一般都会同一台电脑用2个或者以上显示器…

Docker的安装和使用

目录 安装Docker 安装yum工具 更新本地镜像源 安装docker 启动docker 关闭防火墙 docker启动命令 配置镜像加速 docker的使用 拉取nginx 查看本地镜像 把镜像文件nginx导出成tar文件 查看是否导出成功 ​编辑 删除本地镜像nginx:latest 导入镜像文件nginx 拉取…

算法基础之快速幂求逆元

快速幂求逆元 核心思想&#xff1a; 逆元&#xff1a; 逆元 ap-2 mod p #include<iostream>#include<algorithm>using namespace std;typedef long long LL;LL pmi(int a,int b,int c){LL res 1;while(b){if(b & 1) res res * a %c;b >> 1;a (LL)…

java定义三套场景接口方案

一、背景 在前后端分离开发的背景下&#xff0c;后端java开发人员现在只需要编写接口接口。特别是使用微服务开发的接口。resful风格接口。那么一般后端接口被调用有下面三种场景。一、不需要用户登录的接口调用&#xff0c;第二、后端管理系统接口调用&#xff08;需要账号密…

基于vue与three.js,监听FPX(Stats类使用)

第一步&#xff0c;引入stats类并new出来 import Stats from three/examples/jsm/libs/stats.module.js; data(){return {stats : new Stats(),} } 第二步&#xff0c;添加dom mounted() {this.init3D();this.animate();window.addEventListener("keydown", this.…

Android 自动化测试——Monkey测试

Android自带了很多方便的测试工具和方法&#xff0c;包括我们常用的单元测试、Robotium测试、Monkey测试、MonkeyRunner测试、senevent模拟等。这些方法对于我们编写高质量的APP十分有用。也可以提前暴露我们程序的隐藏问题。今天给大家讲一下Monkey测试&#xff0c;Monkey测试…

Java中的时间日期类⭐️通过具体案例分析下开发中常用到的几种时间日期格式类的使用

小伙伴们大家好&#xff0c;系统中不少模块都要用到时间日期&#xff0c;来分析总结下项目中用到的些日期类 目录 一、时间日期类 1.java.util.Calendar&#xff1a; 2.java.util.Date&#xff1a; 3.java.time.LocalDate、java.time.LocalTime、java.time.LocalDateTime&…