哈希重要思想——位图详解

news2024/11/26 18:23:26

一,概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。
为了方便理解我们引入一道面试题,

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

两个思路:
1.排序+二分
2.set+find
首先40亿个整数占用多少内存?
1G约等于10亿字节,40亿个整数有160亿字节,也就是大约16G左右,
16G是绝对会内存超限的,所以正常的方法我们都不可以使用。

这就要用到位图的思想,我们用比特位去存储数据,一个比特位就是一个数据
因为是存无符号整数,所以最大的数就是2^32,那我们就开这么大的范围。
2^32比特位是多大呢?
是512MB,变小了非常多。
例如50,就如下存储。
在这里插入图片描述

找位置

如何找到相应的位置?
还是以50为例。
1.先找到50在第几个整型中
50/32==1

2.50在这个整型的第几个比特位中
50%32==18

总结:
如何找到x对应的位置

  1. x在第i个整型中 i=x/32
  2. x在这个整型的第j个比特位中 j=x%32

二,模拟实现位图

在这里插入图片描述
这是库中给我们提供的位图,他有比较核心的三个函数组成分别是:set,reset,test。

1.set

set就是把x映射的位标记位1。
在这里插入图片描述
这里用到的位操作是:|

代码

//把x映射的位标记为1
		void set(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			_bits[i] |= (1 << j);
		}

2.reset

和set的作用相反reset是把x映射的位置标记位0
在这里插入图片描述

代码

		//把x映射的位标记为0
		void reset(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			_bits[i] &= ~(1 << j);
		}	

3.test

test查看指定位置是否为1
在这里插入图片描述

代码

		//查看是否有值
		bool test(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			return _bits[i]&(1 << j);//0为假,非0就是真
		}

全代码

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

		//把x映射的位标记为1
		void set(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			_bits[i] |= (1 << j);
		}
		//把x映射的位标记为0
		void reset(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			_bits[i] &= ~(1 << j);
		}	
		//查看是否有值
		bool test(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			return _bits[i]&(1 << j);//0为假,非0就是真
		}
	private:
		vector<int> _bits;
	};

测试

	void test_bitset()
	{
		bitset<100> bs1;
		bs1.set(50);
		bs1.set(30);
		bs1.set(90);

		for (size_t i = 0; i < 100; i++)
		{
			if (bs1.test(i))
			{
				cout << i << "->" << "在" << endl;
			}
			else
			{
				cout << i << "->" << "不在" << endl;
			}
		}
	}

三,面试题(位图拓展)

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

还是先算100亿个整数占多少内存,
1G是10亿字节,100亿个整数大概40G内存
刚刚是统计在不在,而这里是统计次数,那我们的思路就是用两个位图来统计
00:表示出现0次
01:表示出现1次
10:表示2次及以上

实现

1.set

如果是00就把他变成01
如果是01就把他变成10

		// 00 -> 01
		// 01 -> 10
		void set(size_t x)
		{
			// 00 -> 01
			if (_bs1.test(x) == false && _bs2.test(x) == false)
			{
				_bs2.set(x);
			}
			// 01 -> 10
			else if (_bs1.test(x) == false && _bs2.test(x) == true)
			{
				_bs1.set(x);
				_bs2.reset(x);
			}
		}
2.test

这就是单纯的返回值就好了

		int test(size_t x)
		{
			// 00 -> 01
			if (_bs1.test(x) == false && _bs2.test(x) == false)
			{
				return 0;
			}
			// 01 -> 10
			else if (_bs1.test(x) == false && _bs2.test(x) == true)
			{
				return 1;
			}
			else
			{
				return 2;
			}
		}

测试

	void test_bitset2()
	{
		int a[] = { 5,7,9,2,5,99,5,5,7,5,3,9,2,55,1,5,6 };
		two_bit_set<100> bs;
		for (auto e : a)
		{
			bs.set(e);
		}

		for (size_t i = 0; i < 100; i++)
		{
			//cout << i << "->" << bs.test(i) << endl;
			if (bs.test(i))
			{
				cout << i << endl;
			}
		}
	}

完整代码

	template<size_t N>
	class two_bit_set
	{
	public:
		// 00 -> 01
		// 01 -> 10
		void set(size_t x)
		{
			// 00 -> 01
			if (_bs1.test(x) == false && _bs2.test(x) == false)
			{
				_bs2.set(x);
			}
			// 01 -> 10
			else if (_bs1.test(x) == false && _bs2.test(x) == true)
			{
				_bs1.set(x);
				_bs2.reset(x);
			}
		}
		int test(size_t x)
		{
			// 00 -> 01
			if (_bs1.test(x) == false && _bs2.test(x) == false)
			{
				return 0;
			}
			// 01 -> 10
			else if (_bs1.test(x) == false && _bs2.test(x) == true)
			{
				return 1;
			}
			else
			{
				return 2;
			}
		}
	private:
		bitset<N> _bs1;
		bitset<N> _bs2;
	};

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

两个位图做相与操作

3. 一个文件有100亿个整数,1G内存,设计算法找到出现次数不超过2次的所有整数。

这个就是第一个的变形,我们增加一个11记录三次及以上的数,把1次和2次的整数找出来即可。

4.给定100亿个整数,设计算法找到只出现一次的整数(限制512MB内存)

这题难在限制了内存,100亿整数按照常规位图操作要1G的空间才行。
还是开两个位图,分两次统计,第一次只统计<2^31大小的值
第二次统计剩余的值即可。
在这里插入图片描述

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

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

相关文章

形位公差Overview of GDT

零件公差产生于十九世纪后期&#xff0c;其初衷是为了保证零件的互换性。起初只有尺寸公差。由于 当时的设计部门和制造部门通常都在一起或就在隔壁&#xff0c;因此交流起来非常方便。在当时&#xff0c;给 定的公差一般都很大&#xff0c;因此当时的设备刀具的能力对于保证产…

【C++要哮着学】初识C++,什么是C++?什么是命名空间?什么又是缺省函数?

文章目录 前言1、C简介1.1、什么是C1.2、C起源1.3、C发展 2、C关键字&#xff08;C98&#xff09;3、命名空间3.1、命名空间的定义及使用3.2、命名空间的嵌套3.3、命名空间的三种使用方式3.3.1、加命名空间名称及作用域限定符3.3.2、使用using将命名空间中某个成员引入3.3.3、使…

【MySQL的内外连接】

文章目录 一、内连接二、外连接1.左外连接2.右外连接 一、内连接 基本语法&#xff1a; select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件&#xff1b;&#xff08;这里的and&#xff0c;也可以修改成where&#xff0c;并且建议使用where&#xff0c;逻辑更清晰…

何为LCA(最近共同祖先)?

原篇&#xff1a;&#xff08;ACM算法&#xff09;tarjan算法求LCA - 知乎 (zhihu.com) 顾名思义&#xff0c;就是求两个节点最近的共同祖先&#xff0c;就好比下图&#xff0c;2和3的共同祖先为3&#xff0c;2和4的共同祖先为1。 关于LCA求解有3种算法。 1.标记回溯法&#…

2024期从、证从、基从、银从备考资料及互助交流群

快进&#xff0c;2024年 金融考证备考资料及互助交流备考群&#xff0c;考啥进啥 5.12 &#xff08;基从&#xff09;基金从业资格考试 5.18&#xff08;期从&#xff09;期货从业资格考试 6.1 &#xff08;证从&#xff09;证券从业资格考试 6.1&#xff5e;6.2&#xff08;银…

MySQL从入门到高级 --- 6.函数

文章目录 第六章&#xff1a;6.函数6.1 聚合函数6.2 数学函数6.3 字符串函数6.4 日期函数6.4.1 日期格式 6.5 控制流函数6.5.1 if逻辑判断语句6.5.2 case when语句 6.6 窗口函数6.6.1 序号函数6.6.2 开窗聚合函数6.6.3 分布函数6.6.4 前后函数6.6.5 头尾函数6.6.6 其他函数6.7 …

Offer必备算法38_贪心算法四_八道力扣题详解(由易到难)

目录 ①力扣56. 合并区间 解析代码 ②力扣435. 无重叠区间 解析代码 ③力扣452. 用最少数量的箭引爆气球 解析代码 ④力扣397. 整数替换 解析代码1_递归改记忆化搜索 解析代码2_贪心策略 ⑤力扣354. 俄罗斯套娃信封问题 解析代码1_动态规划&#xff08;超时&#xf…

SAP-MM-BASIS-采购审批权限配置

如何控制某用户的审批权限? 例如: 公司人员变动,审批流从原来的经理审批、总监审批,现在要改成主管审批、经理审批。 1、权限变更 1.1、找到配置路径 以经理为例:SU01 点击查看/更改, 点击角色,找到审批文件,双击角色ZM0019_3000 上图可以控制采购订单的凭证类型和…

AVL树的完全指南:平衡与性能

文章目录 AVL树简介AVL的操作建立一个AVL树插入操作删除操作 书写代码1.构造函数和析构函数2.获取最大值和最小值3.树的高度和节点个数3.前序中序和后序遍历4.判断树是否为空树5.四个旋转操作6.获取平衡因子7.插入操作8.删除操作9.搜索节点.h文件中的定义 总结 AVL树简介 AVL树…

企业邮箱是什么?怎么申请一个企业邮箱

企业邮箱是什么&#xff1f;企业邮箱包含着许多企业需要的功能&#xff0c;包含统一创建签名、大容量存储、域名定制等功能&#xff0c;这些功能能够帮助企业更好地展示企业的专业形象以及更好得协作办公。本文将详细介绍企业邮箱的概念、特征和企业邮箱的申请步骤。 一、企业…

EasyExcel 中实体类的注解@ExcelProperty

ExcelProperty(value "职务", index 0) value 与index 的优先级, 实测得出下面结论. 1、只有value : 按照value 的匹配 2、只有index: 按照index 的匹配 3、 同时有value和index: 按照index的匹配. 结果: 按照index的匹配, 找到的数据 {"administrat…

Django 安全性与防御性编程:如何保护 Django Web 应用

title: Django 安全性与防御性编程&#xff1a;如何保护 Django Web 应用 date: 2024/5/13 20:26:58 updated: 2024/5/13 20:26:58 categories: 后端开发 tags: CSRFXSSSQLUploadHTTPOnlyPasswordSession 跨站请求伪造&#xff08;CSRF&#xff09; 跨站请求伪造&#xff0…

TriCore:Interrupt 2

今天继续来看看 IR 模块。 名词缩写 缩写全称说明IRInterrupt Router SRService Request 包括&#xff1a; 1. External Resource 2. Internal Resource 3.SW&#xff08;Software&#xff09; SPService Privoder 包括&#xff1a; 1. CPU 2. DMA SRNService Request NodeS…

宁夏银川最牛起名大师的老师颜廷利:宝与饱,饿跟恶

对于中国优秀传统文化之根-汉语而言&#xff0c; 恶&#xff0c;对应着‘饿’&#xff1b; 宝&#xff0c;对应着‘饱’… 由此可见&#xff0c;无论是‘饿’&#xff08;与‘恶’同音&#xff09;&#xff0c;还是‘饱’&#xff08;与‘宝’通音&#xff09;&#xff0c;实际…

GDPU unity游戏开发 角色控制器与射线检测

在你的生活中&#xff0c;你一直扮演着你的角色&#xff0c;别被谁控制了。 小试 1. 创建一个角色控制器&#xff0c;通过键盘控制角色控制器的移动&#xff0c;角色控制器与家具发生碰撞后&#xff0c;通过Debug语句打印出被碰撞物体的信息(搜索OnControllerColliderHit的使用…

GO语言核心30讲 实战与应用 (WaitGroup和Once,context,Pool,Map,字符编码,string包,bytes包)

原站地址&#xff1a;Go语言核心36讲_Golang_Go语言-极客时间 一、sync.WaitGroup和sync.Once 1. sync.WaitGroup 比通道更加适合实现一对多的 goroutine 协作流程。 2. WaitGroup类型有三个指针方法&#xff1a;Wait、Add和Done&#xff0c;以及内部有一个计数器。 (1) Wa…

【报错合集】完美解决“虚拟机使用的是此版本 VMware Workstation 不支持的硬件版本”

文章目录 解决方案&#xff1a;更改设置的硬件版本 今天我需要将别人的虚拟机克隆到我的VMware Workstation上运行&#xff0c;结果发生了以下的错误&#xff1a; 刚开始以为是VMware Workstation的版本问题太低导致的&#xff0c;所以我删除了原来的那个版本&#xff0c;下载…

51cto已购买的视频怎么下载到本地

你是否曾在学习51CTO的精品课程时&#xff0c;希望可以随时随地无网络干扰地进行学习&#xff0c;或是想要将这些已购买的课程永久珍藏&#xff1f;今天&#xff0c;你的愿望将要实现。我们将向你揭示如何轻松地将已购买的51CTO视频下载到本地&#xff0c;让学习的路上再也没有…

企业邮箱域名是什么?怎么注册一个企业邮箱域名?

企业邮箱域名是什么&#xff1f;企业邮箱域名是企业申请的专属域名&#xff0c;绑定专属的邮箱域名&#xff0c;能够在发送邮件时提高品牌识别性、专业性和宣传效果。那么&#xff0c;我们该怎么注册一个企业邮箱域名呢&#xff1f;本文将为你详细介绍。 一、企业邮箱域名是什…

本地搭建各大直播平台录屏服务结合内网穿透工具实现远程管理录屏任务

文章目录 1. Bililive-go与套件下载1.1 获取ffmpeg1.2 获取Bililive-go1.3 配置套件 2. 本地运行测试3. 录屏设置演示4. 内网穿透工具下载安装5. 配置Bililive-go公网地址6. 配置固定公网地址 本文主要介绍如何在Windows系统电脑本地部署直播录屏利器Bililive-go&#xff0c;并…