【C++】哈希应用之位图

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》

🌝每一个不曾起舞的日子,都是对生命的辜负


目录

前言

1.位图的概念

2.位图的模拟实现

2.1构造

2.2set

2.3reset

2.4test

3.源码

4.位图应用变形 


前言

哈希是一种解决问题的思想,那么有关哈希的一个重要应用便是位图,该种结构适用于海量数据,数据无重复的场景,通常用来判断某个数据存在或者不存在,但只能处理整型数据。


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。 

=========================================================================

GITEE相关代码:🌟樊飞 (fanfei_c) - Gitee.com🌟

=========================================================================


1.位图的概念

我们以一道面试题引入:

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

---『 腾讯』

根据题意,给定40亿个数,很明显如果是40亿个整型,1GB=10亿byte,40亿个整型=160亿byte=16GB,内存中根本放不下,那么这道题关键就在于只需要判断这个数是否在,所以我们仅需一个『 比特位』就可以表示某个数的状态,如果二进制比特位为1,代表存在,为0代表不存在。

而无符号整数总共有2^32个,因此我们仅需2^32个比特位=512M的内存空间就可以判断一个数是否在。

这种思想就是利用了哈希应用中的位图。


2.位图的模拟实现

很明显,位图的底层就是数组,那么我们就选用vector来当作底层容器。

2.1构造

在构造位图时,我们需要根据所给位数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);
}

2.2set

set用于设置位,即设置某个数为存在,所处位设置为1。

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

  1. 计算出该位位于第 i 个整数的第 j 个比特位。
  2. 将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(不影响其他位)
}

2.3reset

reset用于清空位,即设置某个数为不存在,所处位设置为0。

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

  1. 计算出该位位于第 i 个整数的第 j 个比特位。
  2. 将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(不影响其他位)
}

2.4test

test用于检验位,即判断某个数是否存在,检验所处位设置的值。

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

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

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

    //算出pos映射的位在第i个整数的第j个位
    size_t i = x / 32;
    size_t j = x % 32;

    return _bits[i] & (1 << j);
}

3.源码

#pragma once
namespace bit_set
{
	template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_bits.resize(N / 32 + 1, 0);
		}

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

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

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

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

		bool test(size_t x)
		{
			assert(x <= N);

			size_t i = x / 32;
			size_t j = x % 32;

			return _bits[i] & (1 << j);
		}
	private:
		vector<int> _bits;
	};
}

4.位图应用变形 

问:1个文件有100亿个int,1G内存,设法找到出现次数不超过2次的所有整数。

这种问题很明显就是利用位图来解决,可是前面的问题我们只需要一个比特位就能标识出一个数字是否存在。

那么这个问题呢?

我们可以设想为3种状态,出现0次、出现1次、出现2次、出现3次及以上。

分别用如下数字标识:

出现0次:00

出现1次:01

出现2次:10

出现3次及以上:11

所以设计结构如下:

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

		bool test(size_t x)
		{
			if (_bs1.test(x) == false
				&& _bs2.test(x) == true)
			{
				return true;
			}

			return false;
		}
	private:
		bitset<N> _bs1;
		bitset<N> _bs2;
	};
}

给定两组数据找交集,我们也可以通过双位图这种思想实现。


=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

=========================================================================

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

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

相关文章

一道很有意思的题目(考初始化)

这题很有意思&#xff0c;需要你对初始化够了解才能解出来 &#xff0c;现在我们来看一下吧。 这题通过分析得出考的是初始化。关于初始化有以下知识点 &#xff08;取自继承与多态&#xff08;继承部分&#xff09;这文章中&#xff09; 所以根据上方那段知识点可知&#xf…

【linux网络(一)】初识网络, 理解四层网络模型

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux网络 1. 前言2. 初识网络…

【Java程序设计】【C00361】基于Springboot的考勤管理系统(有论文)

基于Springboot的考勤管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 项目获取 &#x1f345;文末点击卡片获取源码&#x1f345; 开发环境 运行环境&#xff1a;推荐jdk1.8&#xff1b; 开发工具&#xff1a;eclipse以及idea&…

RWTH-PHOENIX Weather数据集模型说明和下载

RWTH-PHOENIX Weather 2014 T数据集说明: 德国公共电视台PHOENIX在三年内(2009 年至 2011 年) 录制了配有手语翻译的每日新闻和天气预报节目,并使用注释符号转录了 386 个版本的天气预报。 此外,我们使用自动语音识别和手动清理来转录原始德语语音。因此,该语料库允许训练…

电脑控制面板在哪?5招教你快速打开!

“我在执行一个任务时要进入电脑的控制面板中查看&#xff0c;但是我不知道电脑的控制面板在哪&#xff0c;谁能帮帮我呀&#xff1f;” 电脑控制面板是一个系统文件夹&#xff0c;它提供了各种对计算机系统进行设置和管理的工具。控制面板允许用户查看并操作基本的系统设置&am…

SAP ABAP-BOPF基础训练-01简介与架构

1. 介绍-Introduction ① BOPF是什么&#xff1f;BOPF(the Business Object Processing Framework)&#xff1a;业务对象处理框架 提供了一种增量和模块化的方法&#xff0c;以符合企业面向服务体系结构(eSOA)的方式实现业务对象&#xff1b; 部分平台基础层&#xff0c;软件组…

【笔记】深入理解JVM机制

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 JVM 运⾏流程图 JVM 中内存区域划分 方法区 / 元数据区 堆 栈 程序计数器 本地方法栈 内存区域总结 JVM 中类加载过程 …

python网络爬虫实战教学——requests的使用(2)

文章目录 专栏导读1、POST请求2、响应3、Cookie设置 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫实战教学》&#xff0c;本专栏针对大学生、初级数据分析工程…

【c++】类和对象(二)this指针

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本节内容来到类和对象第二篇&#xff0c;本篇文章会带领大家了解this指针 目录 1.this指针1.1this指针的引出1.2this指针的特性1.3思考题1.4C语言和C实现Stack的对…

Qt|多线程串口通信

前篇:Qt|串口通信之同步数据收一包发一包数据 文章目录 创建工程添加串口通信类添加线程类主函数运行结果需求:串口下方的一些耗时操作并不想阻塞主进程的推进; 环境:windows10+VS2017+Qt5.14.2; 写在最前: 串口不支持跨线程操作,需要写信号槽形式传递;选择COM口下发指…

Allegro之轻松绕等长

如大家所见&#xff0c;这个世界通信的速率越来越快&#xff0c;生活的节奏也在飞驰&#xff0c;如果工作还是慢条斯理&#xff0c;你将是下一个淘汰的人。 高速PCB设计避免不了的要绕等长&#xff0c;然而有时候一个单板需要绕等长的总线很多&#xff0c;一个一个的绕下去&…

独立游戏《星尘异变》UE5 C++程序开发日志3——UEC++特供的数据类型

本篇日志将介绍FString&#xff0c;FText、FName的用法和相互转换&#xff0c;以及容器TMap&#xff0c;TArray的增删查改 一、字符串相关数据类型&#xff1a;FString、FText、FName FString是最接近std::string的类型&#xff0c;字符串本身可以看做一个存储char型的动态数…

数据容器-dict以及总结-Python

师从黑马程序员 字典的定义 同样使用{},不过存储的元素是以个个的&#xff1a;键值对&#xff0c;如下语法&#xff1a; #定义字典 my_dict1{"王力宏":99,"周杰伦":88,"林俊杰":77} #定义空字典 my_dict2{} my_dict3dict() print(f"字典1…

HarborCDN技术分析

一、介绍 简要介绍 ​​Harbor​​ 是由VMware公司开源的企业级的Docker Registry管理项目&#xff0c;它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支持等功能。Harbor 的所有组件都在 Dcoker 中部署&#xff0c;所以 Harbor 可使用 Docker C…

直击GDC 2024 (二):网易智企首次揭秘篮球游戏AI智能体!

近几日&#xff0c;世界级游戏开发者盛会 GDC&#xff08;Game Developers Conference&#xff09;2024 正在美国举行。全球游戏行业的精英都汇聚于旧金山&#xff0c;共同探索前沿的游戏开发技术、洞察行业最新趋势&#xff0c;并互相交流对未来发展的深刻见解。 &#xff08;…

校园跑腿小程序源码系统多校园版 跑腿达人入驻接单 带完整的安装代码包以及系统部署教程

在数字化时代的浪潮中&#xff0c;校园生活的便捷性和高效性成为了广大师生的共同追求。为了满足这一需求&#xff0c;罗峰给大家分享一款适用于多校园的跑腿小程序源码系统——校园跑腿小程序源码系统多校园版。该系统不仅提供了完整的安装代码包&#xff0c;还附带了详尽的系…

你以为的富贵包其实是脂肪瘤 整形外科医生为你科普脂肪瘤

脖子后面长的包都是富贵包吗&#xff1f;西安国际医学中心医院整形外科门诊主任冯登超曾接诊过一位七旬老人&#xff0c;他的颈部后方有一个直径约13厘米的巨大肿块&#xff0c;核磁共振检查提示右侧劲后皮下软组织内巨大囊性肿块。“他们以为是富贵包&#xff0c;实际上是脂肪…

C语言:给结构体取别名的4种方法

0 前言 在进行嵌入式开发的过程中&#xff0c;我们经常会见到typedef这个关键字&#xff0c;这个关键字的作用是给现有的类型取别名&#xff0c;在实际使用过程中往往是将一个复杂的类型名取一个简单的名字&#xff0c;便于我们的使用。就像我们给很熟的人取外号一样&#xff…

【大模型基础】什么是KV Cache?

哪里存在KV Cache&#xff1f; KV cache发生在多个token生成的步骤中&#xff0c;并且只发生在decoder中&#xff08;例如&#xff0c;decoder-only模型&#xff0c;如 GPT&#xff0c;或在encoder-decoder模型&#xff0c;如T5的decoder部分&#xff09;&#xff0c;BERT这样…

详解Python面向对象编程(一)

类和对象 面向过程——怎么做&#xff1f; &#xff08;1&#xff09;把完成某一需求的所有步骤、从头到尾&#xff0c;逐步实现 &#xff08;2&#xff09;根据开发需求&#xff0c;将某些功能独立的代码块封装成一个又一个的函数 &#xff08;3&#xff09;最后完成的代码&a…