哈希的应用——位图

位图

题目思考

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

看到这个问题可能会想到这样的思路:

1. 遍历, 时间复杂度O(N)
2. 排序 + 二分查找
3. 利用哈希表或红黑树, 就是放到set或unordered_set里面进行查找.

上面这些方法有没有什么问题?

我们注意到它这里给了40亿个整数,而1G=1024MB=1024*1024KB=1024*1024*1024byte, 1G约等于10亿byte, 40亿个整数约等于16G.

上面那些方法最关键的问题是16G的数据可能都不能一次全部放到到内存中, 内存可能都不够用.
二分查找的话排序不是问题, 如果要排序那就是用归并排序了, 分开放到一个个的小文件里面, 进行归并, 但是关键是内存开不出这么大的连续重进, 不能支持下标访问, 无法用二分查找.
放到set或unordered_set里面查找也是一样, 内存可能不够, 哈希表或红黑树还有额外的消耗, 因为还要存一些其它的成员变量, 可以分开每次处理一小部分, 但这样效率就不太行了。

所以上面这些思路都不太合适,而且我们这里只是要判断在不在,其实没必要把它们全部存起来。


位图概念 

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

数据是否在给定的整形数据中, 结果是在或者不在, 刚好是两种状态, 那么可以使用一个二进制比特位来代表数据是否存在的信息, 如果二进制比特位为1代表存在, 为0代表不存在.

比如:

回到题目, 题目说的是40亿个不重复的无符号整数, 无符号整数的最大值是2^32-1,即4294967295, 所以需要2^32次方个比特位, 需要的内存就是上面的16G/32 = 0.5G, 0.5G内存就可以开辟出来了.

所以我们开这样一个数组. 每个元素的大小是1个比特位, 因为我们用1个比特位就可以来标识当前位置下标所对应的值存不存在, 所以它其实就是一个直接定址法的思想.

没有类型的大小是1个bit, 所以我们可以开成int类型或者char类型的数组, 类型无所谓, 只要能取到对应的比特位即可.这里0到31映射到第一个int, 32到63映射到第二个int, 以此类推.


实现位图 

位图结构与构造函数 

模板参数可以用一个非类型模板参数作为位图所表示的数据个数. 

内部的vector应该开辟多大的空间?

这里的N是需要表示的数据个数, 在位图中就是N个比特位, N/8*4就是对应的整型个数, 但是可能不是整除会有剩余, 所以还需要+1. 而且初始的时候要把vector里面全放成0.

set和reset接口实现 

位图中的两个核心操作是setreset:

set就是把x映射的那个位置的比特位设置成1,表示这个数存在, reset就是把它设置成0, 表示不存在.

我们现在开的是int数组, 里面是一个个的int(32bit), 所以我们首先要找到数据x映射到第几个int, 然后找数据x映射到这个int的哪一位.

设i是映射的那个int, j是int里的位数, i = x/32, j = x%32, 最终x = i*32 + j. 

我们找到了这个比特位, 如何把它设置成1或者0呢?

先来看set, 把x映射的比特位设置成1, 怎么做呢?

其实就是第j位设置为1, 其它位全为0, 假如j是3, 我们可以给这个位置按位或
这样改变这个位置的同时还没有影响其它位置,因为一个数或0还是它本身.
所以我们让x | (1<<j) 就可以.

那reset就是把x映射的比特位设置成0

假设j还是3, 给这个位置按位与1111 1111 1111 1011就可以.
所以我们只需让x & ~(1<<j)的结果按位与就行了, 这样x映射的比特位变成0, 其它位置也不受影响.

test接口实现

除了这两个还有一个比较核心的接口——test, 它是去判断某个值存不存在(它映射的位置是否被设置成了1)

让x映射的这个比特位 和 1<<j 进行按位与, 如果是0, 就表明不存在; 如果是1, 就存在.

 简单测试一下:

void TestBS()
{
	test::bitset<100> bs;
	bs.set(34);
	if (bs.test(34))
		cout << "34存在" << endl;
	else
		cout << "34不存在" << endl;

	bs.reset(34);
	if (bs.test(34))
		cout << "34存在" << endl;
	else
		cout << "34不存在" << endl;
}

 

执行set后:

 执行reset后:


回到最开始的那道题, 40亿个无符号整数, 我们的位图应该给多大?

开40亿个可以吗, 不可以.
要注意我们不能按个数去开, 而是要按照范围去开.
就算现在变成10亿个无符号整数, 我们也应该开4294967295(即2^32-1,无符号整型最大值)个, 因为我们不知道这10亿个整数的取值范围, 它可能就包含了最大值, 所以我们要确保不论它多大, 就可以映射到位图中一个确定的位置上.

test::bitset<-1> bs;

位图其实C++STL库里面有提供的现成的:


位图的应用(海量数据处理)

下面来看几个位图相关的题:

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

首先这里是100亿个整数, 我们还开0xFFFFFFFF这么多空间吗?
虽然有100亿个, 但它的范围还是不变的, 不会超过整型最大值, 只能说明有很多重复值. 那我们还是用位图来解决, 找出只出现一次的整数, 1个比特位只能找出是否存在, 2个比特位就可以判断出现的次数了:

我们只看两位, 00就是0次, 01就是一次, 10是1次以上, 不一定就是两次, 因为我们set的时候如果是10就可以不进行操作了, 因为找的是只出现一次的整数, 只要证明出现不是1次就行.
1.我们可以给上面实现的位图改造一下, 改造成每个位置占两个比特位的位图.
2. 也可以不改造, 我们还是用上面的位图, 但是我们开两个位图.

所以我们可以封装一个Twobitset: 

两个位图中映射位置的值:

如果是00, 就变成01;

如果是01, 就变成10;

如果是10, 已经超过1次了, 就可以不处理了,因为已经能判断出来出现不止1次.

template<size_t N>
class Twobitset
{
public:
	void set(size_t x)
	{
		if (!bs1.test(x) && !bs2.test(x))
			bs2.set(x);
		else if (!bs1.test(x) && bs2.test(x))
		{
			bs2.reset(x);
			bs1.set(x);
		}
		//其它的情况不需要再处理了, 因为已经能判断出来出现不止1次
	}

	void PrintOnce()
	{
		for (size_t i = 0; i < N; i++)
		{
			//打印只出现一次的
			if (!bs1.test(i) && bs2.test(i))
				cout << i << " ";
		}
	}
private:
	bitset<N> bs1;
	bitset<N> bs2;
};

测试:

void TestBS3()
{
	int a[] = { 1,4,7,9,44,88,1,4,88,99,78,5,7 ,7,7,7 };
	test::Twobitset<100> bs; //假设数据范围只有100
	for (auto e : a)
	{
		bs.set(e);
	}
	bs.PrintOnce();
}

 


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

把两个文件的数据分别映射到两个位图里面, 然后遍历其中一个文件依次取值, 如果一个值在两个文件里都存在, 就是交集.

void TestBS4()
{
	size_t N = 100; //假设数据范围是0~100
	int a[] = { 1,4,7,9,44,88,1,4,88,99,78,5,7 ,7,7,7 };
	int b[] = { 1,4,7,10,44,88,1,4,88,100,60,5,7 ,7,7,7 };

	test::bitset<100> bs1;
	test::bitset<100> bs2;

	for (auto e : a)
	{
		bs1.set(e);
	}

	for (auto e : b)
	{
		bs2.set(e);
	}

	for (size_t i = 0; i< N;i++)
	{
		if (bs1.test(i) && bs2.test(i))
			cout << i <<" ";
	}
}

 


3. 位图应用变形: 1个文件有100亿个int, 我们只有1G内存, 设计算法找到出现次数不超过2次的所有整数.

只需要把第一个题的双位图改一改即可, 最后打印出01和10即可:

template<size_t N>
class Twobitset
{
public:
	void set(size_t x)
	{
		if (!bs1.test(x) && !bs2.test(x))
			bs2.set(x);
		else if (!bs1.test(x) && bs2.test(x))
		{
			bs2.reset(x);
			bs1.set(x);
		}
		else if (bs1.test(x) && !bs2.test(x))
		{
			bs2.set(x);
		}
		//11就是出现3次及以上了
	}

	void PrintOnce()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (!bs1.test(i) && bs2.test(i) || bs1.test(i) && !bs2.test(i))
				cout << i << " ";
		}
	}
private:
	bitset<N> bs1;
	bitset<N> bs2;
};
void TestBS5()
{
	int a[] = { 1,4,7,9,44,88,1,4,88,99,78,5,7 ,7,7,7 };
	test::Twobitset<100> bs;
	for (auto e : a)
	{
		bs.set(e);
	}
	bs.PrintOnce();
}


关于搜索的总结: 

1.暴力查找: 数据量大了, 效率就低.

2.排序 + 二分查找 

问题a: 排序有代价

问题b: 数组不方便增删

3. 搜索树: 引申出->ALV树和红黑树, 性能整体比较稳定, 插入不会有太大波动.

4. 哈希: 搜索比较快, 但是整体不稳定, 插入是有波动的, 某次的插入可能需要扩容, 扩容代价比较高

还有极端场景下某个桶的数量可能很高, 但可以改挂红黑树解决.

以上数据结构, 空间消耗很高.

 对于数量很大的数据的场景?
5、[整形]的是否存在及其扩展问题--位图及变形节省空间, 但是位图的局限是只能处理整型.

6、[其他类型]的存在问题呢?--布隆过滤器, 下面会介绍.


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

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

相关文章

10个顶级Linux开源反向代理服务器 - 解析与导航

反向代理服务器是一种部署在客户端和后端/源服务器之间的代理服务器&#xff0c;例如 NGINX、Apache 等 HTTP 服务器或用 Nodejs、Python、Java、Ruby 编写的应用程序服务器、PHP 和许多其他编程语言。 它是一个网关或中间服务器&#xff0c;它接受客户端请求&#xff0c;将其传…

【MATLAB】LMD分解+FFT+HHT组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 LMDFFTHHT组合算法是一种基于局部均值分解&#xff08;LMD&#xff09;、快速傅里叶变换&#xff08;FFT&#xff09;和希尔伯特-黄变换&#xff08;HHT&#xff09;的组合算法。 LMD是…

Python实现WOA智能鲸鱼优化算法优化LightGBM分类模型(LGBMClassifier算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 鲸鱼优化算法 (whale optimization algorithm,WOA)是 2016 年由澳大利亚格里菲斯大学的Mirjalili 等提…

Spatialite获取点线面集合的中心点

在这里插入代码片sql SELECT ST_AsText(ST_Centroid(ST_GeomFromText(GEOMETRYCOLLECTION(LINESTRING(105.400538 26.965642, 105.376419 26.938482, 105.350328 26.911685, 105.329089 26.879879, 105.313625 26.84789, 105.301742 26.813179, 105.292141 26.775107, 105.2858…

【技巧】Excel表格如何退出“只读方式”?

如果Excel表格被设置了“只读模式”&#xff0c;那每次打开Excel都会出现对话框提示是否以“只读方式”打开&#xff0c;并且以“只读方式”打开的Excel&#xff0c;如果进行更改是无法保存原文件的。那要如何退出“只读方式”呢&#xff1f; 首先&#xff0c;我们要看下Excel表…

策略算法与Actor-Critic网络

策略算法 教程链接 DataWhale强化学习课程JoyRL https://johnjim0816.com/joyrl-book/#/ch7/main 策略梯度 与前面的基于价值的算法不同&#xff0c;这类算法直接对策略本身进行近似优化。 在这种情况下&#xff0c;我们可以将策略描述成一个带有参数 θ θ θ的连续函数…

java小游戏之【王者荣耀】

首先创建一个新的Java项目命名为“王者荣耀”&#xff0c;并在src下创建两个包分别命名为“com.sxt"、”com.stx.beast",在相应的包中创建所需的类。 代码 package com.sxt;import javax.swing.*; import java.awt.*;public class Background extends GameObject {p…

Qt_一个由单例引发的崩溃

Qt_一个由单例引发的崩溃 文章目录 Qt_一个由单例引发的崩溃摘要关于 Q_GLOBAL_STATIC代码测试布局管理器源码分析Demo 验证关于布局管理器析构Qt 类声明周期探索更新代码获取父类分析Qt 单例宏源码 关键字&#xff1a; Qt、 Q_GLOBAL_STATIC、 单例、 UI、 崩溃 摘要 今…

深入解析:Peft Adapter与LLM融合

在增量预训练阶段或有监督微调阶段使用高效微调方法(Lora)时会产生adapter文件,相当于是一个“补丁”。那么如何将“补丁”与原始模型合并呢? 下面将对模型合并代码进行解读。 相关代码将全部上传到github: https://github.com/hjandlm/LLM_Train 欢迎关注公众号 代码…

HarmonyOS应用开发者基础认证【题库答案】

HarmonyOS应用开发者高级认证【题库答案】 一、判断 首选项preferences是以Key-Value形式存储数据&#xff0c;其中Key是可以重复。&#xff08;错&#xff09;使用http模块发起网络请求时&#xff0c;必须要使用on(‘headersReceive’&#xff09;订阅请求头&#xff0c;请…

PHP 双门双向门禁控制板实时监控源码

本示例使用设备&#xff1a; 实时网络双门双向门禁控制板可二次编程控制网络继电器远程开关-淘宝网 (taobao.com) <?PHPheader("content-type:text/html;charsetGBK");$ThisIpget_local_ip(); //获取电脑IP地址 $server udp://.$ThisIp.:39192; $sock…

每天一道算法题:51. N 皇后

难度 困难 题目 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n_ _皇后问题 的…

FPGA模块——AD高速转换模块(并行输出转换的数据)

FPGA模块——AD高速转换模块&#xff08;并行输出转换的数据&#xff09; &#xff08;1&#xff09;AD9280/3PA9280芯片&#xff08;2&#xff09;代码 &#xff08;1&#xff09;AD9280/3PA9280芯片 AD9280/3PA9280芯片的引脚功能&#xff1a; 工作电压2.7到5.5v 数据对应&a…

代码随想录算法训练营第五十七天|739. 每日温度、496.下一个更大元素 I

LeetCode 739. 每日温度 题目链接&#xff1a;739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 单调栈开始&#xff0c;为什么要用栈&#xff0c;因为栈是先入后出&#xff0c;当我们遍历从前往后的时候&#xff0c;每次遍历的元素都是添加至栈尾&#xff0c;方便我们进…

西南科技大学电路分析基础实验A1(一阶电路的设计)

目录 一、实验目的 二、实验设备 三、预习内容(如:基本原理、电路图、计算值等) 四、实验数据及结果分析(预习写必要实验步骤和表格) 1. 观测一阶电

搜索百度可以直接生成代码拉

先看效果图&#xff1a; 使用示例&#xff1a; 比如我要搜索“JS取一个数在两个数更近”的方法&#xff0c;直接搜“JS取一个数在两个数更近”&#xff0c;点击百度一下&#xff0c;就会出现想要的代码&#xff0c;如上图。

《金融科技行业2023年专利分析白皮书》发布——科技变革金融,专利助力行业发展

金融是国民经济的血脉&#xff0c;是国家核心竞争力的重要组成部分&#xff0c;金融高质量发展成为2023年中央金融工作的重要议题。《中国金融科技调查报告》中指出&#xff0c;我国金融服务业在科技的助力下&#xff0c;从1.0时代的“信息科技金融”、2.0时代的“互联网金融”…

坚鹏:数字金融——金融行业的数字科技和智能化转型培训圆满结束

中国邮政储蓄银行拥有优良的资产质量和显著的成长潜力&#xff0c;是中国领先的大型零售银行。2016年9月在香港联交所挂牌上市&#xff0c;2019年12月在上交所挂牌上市。中国邮政储蓄银行拥有近4万个营业网点&#xff0c;服务个人客户超6.5亿户。2022年&#xff0c;在《银行家》…

力扣hot100 滑动窗口最大值 单调队列

&#x1f468;‍&#x1f3eb; 题目地址 &#x1f37b; AC code class Solution {public int[] maxSlidingWindow(int[] nums, int k){int n nums.length;int[] res new int[n - k 1]; // 单调递减队列int[] q new int[n];// q数组维护的是元素在 nums 数组对应的下标int…

从零构建属于自己的GPT系列1:预处理模块(逐行代码解读)、文本tokenizer化

1 训练数据 在本任务的训练数据中&#xff0c;我选择了金庸的15本小说&#xff0c;全部都是txt文件 数据打开后的样子 数据预处理需要做的事情就是使用huggingface的transformers包的tokenizer模块&#xff0c;将文本转化为token 最后生成的文件就是train_novel.pkl文件&a…