哈希表——位图

哈希表——位图

  • 基本概念
  • 一道面试题
  • 位图实现
    • 设置存在或不存在
    • 检查存在
  • 解决一开始的问题

之前我们已经了解了哈希表的底层实现,今天我们来了解一下哈希表思想的衍生产物——位图

基本概念

在了解位图之前,我们先来了解一些简单的概念。
我们都知道,计算机的数据和指令都是二进制的:
在这里插入图片描述二进制里面只有0,1两个数字,这就意味着这两个数字可以表示两个不同的状态,位图的基础也是基于这一点。

所以,我们利用这一点。假设我们现在有一堆海量的数据,我可以通过某种方式让数字对应到相应的二进制的数位上,同时规定0表示该数字不存在,1表示存在。这本质上也是一种哈希的思想。

在这里插入图片描述

一道面试题

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

如果我们硬来,先将这40亿个数存起来,然后二分查找。这无疑是不行的。因为,光是把数据存起来就要用掉16GB的内存,而且要求空间连续。

在这里插入图片描述
但是这时候我们用位图,让位图的每一位对应一个数字,1表示存在,0表示不存在。这样可以大大降低内存的消耗:
在这里插入图片描述
大概我们想要是这样的:
在这里插入图片描述我们所有的位是用来映射存在关系的。

位图实现

我们先来搭搭架子:

#pragma once
#include<iostream>
#include<vector>
using namespace std;

namespace My_bitmap
{
	//N是需要多少bit位
	template<size_t N>
	class bitmap
	{
	public:

	private:
		vector<int> _bmp;
	};
}

现在我们来计算需要多少内存,然后通过构造函数,开出来相应的空间:

#pragma once
#include<iostream>
#include<vector>
using namespace std;

namespace My_bitmap
{
	//N是需要多少bit位
	template<size_t N>
	class bitmap
	{
	public:
		bitmap()
		{
			_bmp.resize(N / 32 + 1, 0); //开出相对应的空间,初始化所有位均为0
		}

	private:
		vector <int> _bmp;
	};
}

设置存在或不存在

现在我们有一个数假设为241,我们要把它设置到位图中,将相应的位设置为1。

因为我们以32位为一组,我们把241 / 32 ,可以得到241被分到了哪一个组,然后241 % 32 的到的是241在第几组的第几位:

在这里插入图片描述
现在我们找到了相应的位置,现在我们要将这个位从0变成1,表示241已经存在,但是我们不能影响其他位的状态,这个时候我们要想起来我们的位运算:
我们来实现一下:
在这里插入图片描述

		bool set(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

			return _bmp[i] |= (1 << j);
		}

好了,我们现在完成了设置,但是现在我想把它又变为0,表示241又不存在了,该怎么办呢?

其实大同小异:
在这里插入图片描述

		bool unset(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

			return _bmp[i] &= (~(1 << j));
		}

检查存在

检查存在和设置存在其实都差不多,只不过把或换成了与:

		bool check(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

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

我们来测试一下:

#include"bitmap.h"

int main()
{
	My_bitmap::bitmap<10000> _bt;

	_bt.set(241);
	cout << "是否存在:" << _bt.check(241) << endl;
	_bt.unset(241);
	cout << "是否存在:" << _bt.check(241) << endl;

	return 0;
}

在这里插入图片描述

解决一开始的问题

现在我们封装好了一个位图,但是我们只是解决了数据的储存,我们还没有解决如何只找出出现一次的数。

其实我们发现,这里面有三种状态:一次都没有出现的,出现一次,出现一次及其以上

我们发现,这也表示状态,这样我们可以用我们上面位图再封装一个位图,这个位图用来寻找只出现了一次的数:

要表示三种状态,我们需要两个位图:
在这里插入图片描述

	//N是需要多少bit位
	template<size_t N>
	class twobit_map
	{
	public:
		void set(size_t x)
		{
			if (_bt1.check(x) == false && _bt2.check(x) == false)
			{
				_bt2.set(x);
			}
			else if (_bt1.check(x) == false && _bt2.check(x) == true)
			{
				_bt1.set(x);
				_bt2.unset(x);
			}
		 }

		void Print()
		{
			for (size_t i = 0; i < N; i++)
			{
				if (_bt1.check(i) == false && _bt2.check(i) == true)
					cout << "出现一次:" << i << endl;
			}
		}
	private:
		bitmap<N> _bt1;
		bitmap<N> _bt2;
	};

测试一下:

	My_bitmap::twobit_map<241> _bt;

	int a[] = { 12,34,45,67,12,90,90,45 };

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

	_bt.Print();

在这里插入图片描述我们依次类推,还可以找出出现了一次和两次的数:

	template<size_t N>
	class twobit_map
	{
	public:
		void set(size_t x)
		{
			if (_bt1.check(x) == false && _bt2.check(x) == false)
			{
				_bt2.set(x);
			}
			else if (_bt1.check(x) == false && _bt2.check(x) == true)
			{
				_bt1.set(x);
				_bt2.unset(x);
			}
			else if (_bt1.check(x) == true && _bt2.check(x) == false)
			{
				_bt2.set(x);
			}
		 }

		void Print()
		{
			for (size_t i = 0; i < N; i++)
			{
				if (_bt1.check(i) == false && _bt2.check(i) == true)
					cout << "出现一次:" << i << endl;
				else if (_bt1.check(i) == true && _bt2.check(i) == false)
					cout << "出现两次" << i << endl;
			}
		}
	private:
		bitmap<N> _bt1;
		bitmap<N> _bt2;
	};

这样,我们可以灵活运用位图完成数据的查找,对于40亿的数据我们只需要开足够大的空间即可:

My_bitmap::twobit_map<0xffffff> _bt;

或者

My_bitmap::twobit_map<-1> _bt;

不过这个方法的保证自己写的代码中都使用的是无符号数,不然会有一点问题。

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

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

相关文章

控制程序执行流程

资源 资源下载 【免费】突破密码认证程序&#xff08;修改函数返回地址&#xff09;资源-CSDN文库 资源内容 源码 在上一篇文章里 修改函数返回地址-CSDN博客 流程 对程序进行编译 思路 了解栈的情况&#xff08;函数地址、缓冲区偏移量&#xff09;程序中密码认证的地…

力扣 第 385 场周赛 解题报告 | 珂学家 | 字典树专场

前言 整体评价 这是一场字典树专场&#xff0c;除了t3这个小模拟之外&#xff0c;1&#xff0c;2&#xff0c;4皆可用字典树搞定。 T4感觉做法挺多的&#xff0c;其实&#xff0c;但是字典树应该效率最高的。 T1. 统计前后缀下标对 I 思路: 模拟 O ( n 2 ) O(n^2) O(n2)全遍…

D3842——三极管驱动,专为脱线和Dc-Dc开关电源应用设计的,起动电流小

D3842/43/44是专为脱线和Dc-Dc开关电源应用设计的恒频电流型Pwd控制器内部包含温度补偿精密基准、供精密占空比调节用的可调振荡器、高增益混放大器、电流传感比较器和适合作功率MOST驱动用的大电流推挽输出颇以及单周期徊滞式限流欠压锁定、死区可调、单脉冲计数拴锁等保护电路…

【RL】Monte Carlo Learning(蒙特卡洛学习)

Lecture 5: Monte Carlo Learning The simplest MC-based RL algorithm: MC Basic 理解MC basic算法的关键是理解如何将policy iteration算法迁移到model-free的条件下。 Policy iteration算法在每次迭代过程中有两步&#xff1a; { Policy evaluation: v π k r π k γ…

山西电力市场日前价格预测【2024-02-16】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-16&#xff09;山西电力市场全天平均日前电价为55.97元/MWh。其中&#xff0c;最高日前电价为314.75元/MWh&#xff0c;预计出现在18:45。最低日前电价为0.00元/MWh&#xff0c;预计出现…

Conda管理Python不同版本教程

Conda管理Python不同版本教程 目录 0.前提 1.conda常用命令 2.conda管理python库 不太推荐 pyenv管理Python不同版本教程&#xff08;本人另一篇博客&#xff0c;姊妹篇&#xff09; 0.前提 ①anaconda、miniconda&#xff08;2个的下载仓库&#xff09;在win上推荐前者&a…

为什么将二维码分解成文字? 二维码在线转文字的方法

将二维码分解成文字的主要目的是为了方便人们获取二维码中的信息便于使用。二维码是一种由黑白方块组成的图案&#xff0c;可以存储大量的数据&#xff0c;如网址、联系方式、产品信息等。然而&#xff0c;对于一些特定的场景或个人需求&#xff0c;无法直接扫描二维码。因此&a…

ubuntu22.04@laptop OpenCV Get Started: 013_contour_detection

ubuntu22.04laptop OpenCV Get Started: 013_contour_detection 1. 源由2. 应用Demo2.1 C应用Demo2.2 Python应用Demo 3. contour_approx应用3.1 读取图像并将其转换为灰度格式3.2 应用二进制阈值过滤算法3.3 查找对象轮廓3.4 绘制对象轮廓3.5 效果3.6 CHAIN_APPROX_SIMPLE v.s…

vue的生命周期图解

vue的生命周期图解 添加链接描述 vue的生命周期函数及过程的简述&#xff1a; vue的生命周期函数&#xff0c;其实就是vm的生命周期&#xff1b; 创建&#xff1a;beforeCreate、created 挂载&#xff1a;beforeMount、mounted 更新&#xff1a;beforeUpdate、updated [ˌʌpˈ…

数字化转型导师坚鹏:数字化思维创新与BLM政府数字化转型战略

数字化思维创新与BLM政府数字化转型战略 ——以BLM模型为核心&#xff0c;践行知行合一思想&#xff0c;实现知行果合一 课程背景&#xff1a; 很多政府存在以下问题&#xff1a; 不知道如何系统地开展数字化转型工作&#xff1f; 不清楚如何高效地执行数字化转型战略&a…

解读OpenAI视频生成模型Sora背后的原理:Diffusion Transformer

Diffusion Models视频生成-博客汇总 前言&#xff1a;OpenAI最近推出的视频生成模型Sora在效果上实现了真正的遥遥领先&#xff0c;很多博主都介绍过Sora&#xff0c;但是深入解读背后原理的博客却非常少。Sora的原理最主要的是核心模型主干《Scalable Diffusion Models with T…

【大厂AI课学习笔记】【2.1 人工智能项目开发规划与目标】(3)数据准备初步

今天来学习数据准备。 一个AI项目要包括构建数据集、数据清理和数据融合、数据采集、特征工程、算法改进和其他步骤。 数据采集和数据清洗&#xff0c;也就是数据准备&#xff0c;要占到人工智能项目一半以上的工作量。 训练的数据量越大&#xff0c;模型越准确。 建立数据标…

php 函数(方法)、日期函数

php 函数、日期函数 1. php函数2. 日期函数 1. php函数 <?php// 创建一个函数 function hello($who) {echo $who.Hello World!; }hello("老张");给参数一个默认值&#xff0c;当然自己有变量走自己的 2. 日期函数 <?php/** date(Y-m-d H:i:s)返回的时间是…

数据库MySQL中出现乱码和表格不对齐怎么解决

MySQL中出现乱码问题及解决办法&#xff1a; 情况类似&#xff1a; 首先进入到数据库中&#xff0c;命令&#xff1a;mysql -h localhost -uroot -p或者mysql -uroot -p;进入数据库后选择一个你的数据库查看表中的中文是否乱码 以上是数据库中表格出现乱码情况&#xff0c;原…

文件上传漏洞--Upload-labs--Pass06--空格绕过

一、什么是空格绕过 在Windows系统中&#xff0c;Windows特性会自动删除文件后缀名后的空格&#xff0c;这使我们看 .php 和 .php 二者没有任何区别&#xff0c;实际上二者是有区别的。若网页源码没有使用 trim()函数 来进行去除空格的操作&#xff0c;就会使网页存在 空格绕…

x86使用内敛汇编实现简单的临界段保护

临界资源保护 实现方法 禁用中断 __attribute__((used)) static inline uint32_t read_eflags (void){uint32_t eflags;ASM_V("pushf\n\tpop %%eax":"a"(eflags));return eflags; } __attribute__((used)) static inline void write_eflags (uint32_t e…

蓝桥杯官网填空题(寻找整数)

问题描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 有一个不超过 10^17 的正整数 n&#xff0c;知道这个数除以 2 至 49 后的余数如下表所示&#xff0c;求这个正整数最小是多少。 运行限制 最大运行时间&#xff1a;…

搭建游戏服务器需要高防御的服务器吗?

随着网络技术的不断发展&#xff0c;游戏行业也迎来了前所未有的发展机遇。然而随着游戏用户的不断增加&#xff0c;游戏服务器的安全问题也日益突出。一些攻击者可能会对游戏服务器进行攻击&#xff0c;例如DDoS攻击、CC攻击等&#xff0c;导致服务器无法正常运行&#xff0c;…

面试经典150题【1-10】

文章目录 面试经典150题【1-10】88. 合并两个有序数组27.移除元素26.删除有序数组中的重复项80.删除有序数组中的重复项II169.多数元素189.轮转数组121.买卖股票的最佳时机1122. 买卖股票的最佳时机 II55.跳跃游戏![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ff…

8.8 矢量图层点要素点聚合(Point cluster)使用

文章目录 前言点聚合&#xff08;Point cluster&#xff09;QGis代码实现 总结 前言 本章介绍如何使用点聚合&#xff08;Point cluster&#xff09;说明&#xff1a;文章中的示例代码均来自开源项目qgis_cpp_api_apps 点聚合&#xff08;Point cluster&#xff09; 点要素过…