【数据结构】哈希应用-STL-位图

news2024/9/20 5:28:48

目录

1、位图的概念

2、位图的设计与实现

 2.1 set

2.2 reset

2.3 test

3、C++库中的位图

4、位图的优缺点

5、位图相关题目

1、位图的概念

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

法一:遍历,时间复杂度是O(N),太慢

法二:排序 + 二分查找。时间复杂度是O(N * logN) + O(logN)。只是第一次比较慢,后面就快了。使用这个方法有一个致命的缺陷是存放40亿个数据需要的内存太过庞大。

1GB = 1024MB = 1024 * 1024KB = 1024 * 1024 * 1024Byte

所以40亿个数据约等于16GB,说明40亿个数据是无法直接放到内存中的,只能放到硬盘文件中。而二分查找只能对内存数组中的有序数据就行查找。这里使用数组是最节省空间的,因为每个位置只存放数据,如果使用红黑树或哈希表需要的空间还要更大

法三:使用位图

数据是否在给定的整型数据中,结果是在或不在,刚好是两种状态,那么可以用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,如果二进制比特位为0,代表不存在。那么我们就可以设计一个用位表示数据是否存在的数据结构,这个数据结构就是位图。

2、位图的设计与实现

实现中要注意的是,C/C++中没有对应位的类型,只能看char/int这样的整型类型,我们再通过位运算去控制对应的比特位。比如我们数据存到vector<int>中,相当于每个Int映射对应的32个值,比如第一个整型映射0~31对应的位,第二个整型映射32~63对应的位,后面依次类推。那么来一个无符号整型x,i = x / 32,j = x % 32,x映射的位置就是vector第i个整型数据的第j位。

我的机器是小端存储,所以一个整型中,低位是在右边

 对于上面40亿个无符号整型,我们开空间需要开2^32个,因为无符号整型有2^32个,不是根据数据个数来开空间 

namespace cxf
{
	template<size_t N> // 开N个位的位图
	class bitset
	{
	public:
		bitset()
		{
			_bs.resize(N / 32 + 1); // 因为N除32可能会除不尽,所以多开一个整型,保证位够
		}
	private:
		std::vector<int> _bs;
	};
}

 2.1 set

向位图中插入数据,也就是将插入数据映射到的位标记成1

假设要向位图中插入数据77,要如何操作呢?

首先计算出位为77的地方位于第几个整型数据的第几个位。会发现位于第3个整型数据的第13个位,然后将1左移13个位的结果与第3个整型数据按位或,就可以将插入数据映射到的位标记成1

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

2.2 reset

向位图中删除数据,也就是将传入数据映射到的位标记成0

假设要向位图中删除数据77,要如何操作呢?

首先计算出位为77的地方位于第几个整型数据的第几个位。会发现位于第3个整型数据的第13个位,然后将1左移13个位再按位取反的结果与第3个整型数据按位与,就可以将插入数据映射到的位标记成0

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

2.3 test

若传入数据映射到的位是1就返回真,是0就返回假

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

可以测试一下

void test_bitset()
{
	cxf::bitset<100> bs; // 开一个100个位的位图
	bs.set(77);
	bs.set(66);
	cout << bs.test(77) << endl;
	cout << bs.test(66) << endl;
	bs.reset(66);
	cout << bs.test(77) << endl;
	cout << bs.test(66) << endl;
}

结果是1 1 1 0,是正确的

那要如何开2^32个空间呢?有3种方法

cxf::bitset<-1> bs1;
cxf::bitset<0xffffffff> bs2;
cxf::bitset<UINT_MAX> bs3;
namespace cxf
{
	template<size_t N> // 开N个位的位图
	class bitset
	{
	public:
		bitset()
		{
			_bs.resize(N / 32 + 1); // 因为N除32可能会除不尽,所以多开一个整型,保证位够
		}
		void set(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			_bs[i] |= (1 << j);
		}
		void reset(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			_bs[i] &= (~(1 << j));
		}
		bool test(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			return _bs[i] & (1 << j);
		}
	private:
		std::vector<int> _bs;
	};
}

3、C++库中的位图

与前面我们自己实现的位图是差不多的,operator[]可以像数组一样控制某个位

要注意,库中的位图是不能直接开2^32个空间的

void test_bitset2()
{
	std::bitset<UINT_MAX> bs;
}

像这样程序会崩溃的,因为我们自己实现的位图底层是使用vector,是去堆上开空间,而库中的位图是用一个静态数组实现的,没办法开太大。我们可以对其就行测试

void test_bitset2()
{
	cxf::bitset<100> bs1;
	cxf::bitset<10000> bs2;
	std::bitset<100> bs3;
	std::bitset<10000> bs4;
	cout << sizeof(bs1) << " ";
	cout << sizeof(bs2) << " ";
	cout << sizeof(bs3) << " ";
	cout << sizeof(bs4) << " ";
}

结果是16 16 16 1256

当然,是可以通过指针来解决的

std::bitset<-1>* ptr = new std::bitset<-1>();

4、位图的优缺点

优点:增删查改快,时间复杂度均为O(1),节省空间

缺点:只适用于整型

5、位图相关题目

位图的应用:

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

注意:此时虽然是100亿个整数,但是还是按范围开空间,所以还是开2^32个位,与前面一样

法一:可以用两个位来标记一个数,00表示没出现过,01表示出现了1次,10表示出现了2次及以上法二:用两个位图,一个数在每个位图中各占一个位,规则与法一相同

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

与上面类似,只不过这里是00表示没出现过,01表示出现了1次,10表示出现了2次,11表示出现3次及以上

我们来复用前面实现的位图来对这两个问题就行实现

namespace cxf
{
	template<size_t N> // 开N个位的位图
	class bitset
	{
	public:
		bitset()
		{
			_bs.resize(N / 32 + 1); // 因为N除32可能会除不尽,所以多开一个整型,保证位够
		}
		void set(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			_bs[i] |= (1 << j);
		}
		void reset(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			_bs[i] &= (~(1 << j));
		}
		bool test(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;
			return _bs[i] & (1 << j);
		}
	private:
		std::vector<int> _bs;
	};

	template<size_t N>
	class twobitset
	{
	public:
		void set(size_t x)
		{
			bool bit1 = _bs1.test(x);
			bool bit2 = _bs2.test(x);
			if (!bit1 && !bit2) // 00->01
			{
				_bs2.set(x);
			}
			else if (!bit1 && bit2) // 01->10
			{
				_bs1.set(x);
				_bs2.reset(x);
			}
			else if (bit1 && !bit2) // 10->11
			{
				_bs2.ser(x);
			}
		}
		int get_count(size_t x) // 返回x出现的次数
		{
			bool bit1 = _bs1.test(x);
			bool bit2 = _bs2.test(x);
			if (!bit1 && !bit2) return 0;
			else if (!bit1 && bit2) return 1;
			else if (bit1 && !bit2) return 2;
			else return 3;
		}
	private:
		bitset<N> _bs1;
		bitset<N> _bs2;
	};
}

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

把数据读出来,分别放到两个位图,依次遍历,同时在两个位图的值就是交集

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

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

相关文章

【Material-UI】按钮组件中的实验性API:Loading按钮详解

文章目录 一、LoadingButton 组件概述1. 组件介绍2. 基本用法 二、LoadingButton 组件的高级用法1. 自定义加载指示器2. 图标与加载位置 三、已知问题与解决方法1. Chrome 翻译工具与 LoadingButton 的兼容性问题 四、实用性与未来展望1. 应用场景2. 未来展望 五、总结 Materia…

共享内存的原理及初识线程

char *str"hello world"; *str-H; 运行时报错&#xff0c;RWX只有R权限。 外设和内存交互以4KB为单位。 虚拟地址32位的划分为10 10 12 前10位对应页表的页目录。 在10位即为页表&#xff0c;页表中存放指定页框的起始物理地址虚拟地址的低12位作为页内偏移。 共…

RedLock算法分析

Redis分布式锁-RedLock算法 手写分布式锁的缺点 Redlock算法设计理念 Redis也提供了Redlock算法&#xff0c;用来实现基于多个实例的分布式锁。 锁变量由多个实例维护&#xff0c;即使有实例发生了故障&#xff0c;锁变量仍然是存在的&#xff0c;客户端还是可以完成锁操作。…

第一篇Linux介绍

目录 1、操作系统 2、Windows和Linux操作系统的区别 3、 Linux 的发行版本 4、 linux 分支 5、 Linux 的含义 6、Linux 特点 1、操作系统 常见操作系统有&#xff1a;Windows、MacOS、Unix/Linux。 类 UNIX Windows&#xff1a;其是微软公司研发的收费操作系统&#xff…

【漏洞复现】JBoss 中间件漏洞

JBoss介绍 JBoss是⼀个基于J2EE的开发源代码的应⽤服务器。JBoss代码遵循LGPL许可&#xff0c;可以在任何商业应⽤中免费使⽤。JBoss是⼀个管理EJB的容器和服务器&#xff0c;⽀持EJB1.1、EJB 2.0和EJB3的规范。但JBoss核⼼服务不包括⽀持servlet/JSP的WEB容器&#xff0c;⼀般…

QTableView使用示例-Qt模型视图委托(MVD)(Model-View-Delegate)

模型视图委托&#xff08;MVD&#xff09;是Qt中特有的设计模式&#xff0c;类似MVC设计模式&#xff0c;将MVC设计模式中的Controller当做MVD中的Delegate&#xff0c;两者的概念基本相同。不同的是委托不是独立存在&#xff0c;而是包含在视图里面。 模型视图委托设计模式中&…

步进电机驱动调试问题

工作中&#xff0c;调试24-byj48步进电机遇到一个怪现象&#xff1a; 1. 偶现 2. 出现问题时其中一个马达反转无法驱动&#xff0c;正转正常。 排查思路&#xff1a; 1. 将两个电机交叉验证&#xff0c;发现始终跟M2接口有关。排除电机问题。 2. 检查电机IO口配置&#xf…

大数据项目——广告数仓之HTTP概述

目录 第一章、理解URL 1.1 客户端、服务器 1.1.1 服务器与服务 1.1.2 客户端 1.2 URL 1.3 查询参数 第一章、理解URL 1.1 客户端、服务器 1.1.1 服务器与服务 所谓服务器&#xff0c;其实就是一台24小时不关机的计算机&#xff0c;它也有自己的cpu、内存、网卡、…

Docker更新镜像源小记

Docker镜像源无法访问 进入docker目录 cd /etc/docker/编辑daemon.json文件&#xff0c;如果没有&#xff0c;则新建 {"registry-mirrors": ["https://dockerproxy.cn"] }收集一些镜像源地址&#xff0c;未测是否能用 “https://hub.uuuadc.top”,“htt…

Android 埋点信息分析——内存篇

源码基于&#xff1a;Android U 0. 前言 在前一篇《Android statsd 埋点简析》一文中简单剖析了Android 埋点采集、传输的框架&#xff0c;本文在其基础对埋点信息进行解析&#xff0c;来看下Android 中埋下的内存信息有哪些。 1. 通过代码剖析google 埋点内容 1.1 PROCESS_M…

网络安全之sql靶场(11-23)

sql靶场&#xff08;11-23&#xff09; 目录 第十一关&#xff08;post注入&#xff09; 第十二关 第十三关 第十四关 第十五关 第十六关 第十七关 第十八关 第十九关 第二十关 第二十一关 第二十二关 第二十三关 第十一关&#xff08;post注入&#xff09; 查看…

echart 制作 Grafana 面板之仪表盘

目录 前言准备工作实现代码代码详解总结相关阅读 前言 Grafana 是一个开源的可视化监控工具&#xff0c;支持多种数据源&#xff0c;并且可以创建丰富的仪表盘。ECharts 是一个强大的开源数据可视化库&#xff0c;通过结合这两者&#xff0c;我们可以创建自定义的仪表盘&…

GPIO输出控制之LED闪烁、LED流水灯以及蜂鸣器应用案例

系列文章目录 STM32之GPIO&#xff08;General Purpose Input/Output&#xff0c;通用型输入输出&#xff09; 文章目录 系列文章目录前言一、LED和蜂鸣器简介1.1 LED1.2 蜂鸣器1.3 面包板 二、LED硬件电路2.1 低电平驱动电路2.2 高电平驱动电路 三、蜂鸣器硬件电路3.1 PNP型三…

使用idea 把一个git分支的部分提交记录合并到另一个git分支上

一、需求 需要将A&#xff08;合并分支&#xff09;分支上的提交记录中的某一次&#xff08;或几次&#xff09;提交合并到B&#xff08;被合并分支&#xff09;分支上 说明&#xff1a;熟练使用idea可以直接看下图即可&#xff0c;若不熟悉可以根据下列步骤进行操作&#xf…

富士乐施5070-V打印机驱动安装

富士乐施5070-V打印机驱动安装 特指打印A3纸张需求&#xff0c;即驱动中能够选择纸张类型&#xff08;安装选择305df驱动只能打印A4类型&#xff09; 富士乐施打印机驱动下载网址&#xff1a; https://m3support-fb.fujifilm-fb.com.cn/driver_downloads/www/ 安装流程&…

C#自定义快捷操作键的实现 - 开源研究系列文章

这次想到应用程序的快捷方式使用的问题。 Windows已经提供了API函数能够对窗体的热键进行注册&#xff0c;然后就能够在窗体中使用这些注册的热键进行操作了。于是笔者就对这个操作进行了整理&#xff0c;将注册热键操作写成了帮助类&#xff0c;并且用此博文来记录这个使用DEM…

【教程】linux-ubuntu安装并配置docker

linux-ubuntu安装并配置docker 一、在线安装1.卸载历史版本情况一&#xff1a;如果之前是手动安装的话&#xff0c;一步一步卸载情况二&#xff1a;通过APT安装 2.使用APT安装&#xff08;推荐&#xff09;(1) 添加https软件包&#xff08;2&#xff09;在apt源中添加docker软件…

kubernets学习笔记——使用kubeadm构建kubernets集群及排错

使用kubeadm构建kubernets集群 一、准备工作1、repo源配置&#xff1a;阿里巴巴开源镜像源2、更新软件包并安装必要的系统工具3、同步时间4、禁用selinux5、禁用交换分区swap6、关闭防火墙 二、安装docker-ce、docker、cri-docker1、安装docker-ce2、开启内核转发&#xff0c;转…

【学习笔记】A2X通信的协议(四)- A2X PC5通信(二)

目录 6.1.2.4 A2X PC5单播链接释放程序 6.1.2.4.1 概述 6.1.2.4.2 发起UE启动A2X PC5单播链接释放程序 6.1.2.4.3 目标UE接受的A2X PC5单播链接释放程序 6.1.2.4.4 发起UE完成的A2X PC5单播链接释放程序 6.1.2.4.5 异常情况 6.1.2.4.5.1 发起UE的异常情况 6.1.2.5 A2X …

Linux——嵌入式学习——C++学习(1)

一、环境配置 由于之前安装过QT&#xff0c;所以直接连接网络之后&#xff0c;运行 运行之后检查安装版本 接着用qt的使用步骤 创建工程即可 三、 1、注释 单行注释&#xff1a;// 多行注释/* */ 2、auto 自动推导类型 2.1声明变量 使…