【C++】哈希的应用——bitset(STL)位图

news2024/12/25 13:05:37

哈希的应用——bitset(STL)位图

文章目录

  • 哈希的应用——bitset(STL)位图
  • 一、bitset的介绍
    • 1.位图的引入
    • 2.位图的概念
    • 3.位图的应用
  • 二、bitset的使用
    • 1.bitset的构造方式
    • 2.bitset成员函数的使用
    • 3.bitset运算符的使用
  • 三、bitset位图的模拟实现
    • 1.位图的基本框架
    • 2.成员函数
      • 2.1.构造函数
      • 2.2.set reset test
      • 2.3.flip count size
      • 2.4.none any all

一、bitset的介绍

在这里插入图片描述

1.位图的引入

看这样一道面试题:

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

单纯从判断一个数是否在一串数字的角度看,我们很容易想到下面的方法:

  1. 把这40亿个整数放到set、unordered_set容器里头,调用find函数来判断
  2. 把这40亿个整数进行外排序,再去二分查找

单就此题而言,为了推翻上面两种方法,首先,我们要清楚40亿个整数,占用多少空间:

image-20230412171813050

  • 计算得知,40亿整数占16个G内存,光数据就占了16G,若放到set容器,其底层红黑树的内部也有负载的消耗(存颜色,三叉连……),再算上16G的消耗,消耗太大了,内存不够,承受不住。同理,内存不够,数据压根放不到内存,也就不能进行排序。

为了解决此问题,这就需要我们用到位图来解决。

  • **我们在判断一个数据是否在给定的整形数据中,结果只有在或者不在这两种状态,那么就可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。这里我们采用直接定址法的哈希,用一个比特位标识映射值在不在,这就是位图。**示例:

image-20230413164600018

对于40亿个整数,我们要我们要开整型的最大值(2^32 - 1)个bit位,大概占500MB的内存:

image-20230412202529485

由此可见,使用位图的方法,大大减少了内存的消耗,并且能很好的解决此问题。


2.位图的概念

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


3.位图的应用

  1. 快速查找某个数据是否在一个集合中
  2. 排序 + 去重
  3. 求两个集合的交集、并集等
  4. 操作系统中磁盘块标记

二、bitset的使用

1.bitset的构造方式

1、使用默认构造函数,构造一个16位的位图,默认初始化为0

bitset<16> bs1;

2、元素按照给定整数的二进制位进行初始化:0xff ——> 1111 1111

bitset<16> bs2(0xfa2);//0000111110100010

3、使用01的string进行初始化:std::string(“01101001”) ——> 01101001

bitset<16> bs3(string("01101001"));//0000000001101001

4、使用01的字符串进行初始化:(“01101001”) ——> 01101001

bitset<16> bs4("01101001");//0000000001101001

2.bitset成员函数的使用

bitset常用成员函数如下表格所示:

成员函数功能
set设置指定位或所有位
reset清空指定位或所有位
flip反转指定位或所有位
test获取指定位的状态
count获取被设置位的个数
size获取可以容纳的位的个数
any如果有任何一个位被设置则返回true
none如果没有位被设置则返回true
all如果所有位都被设置则返回true

示例:

int main()
{
	bitset<16> bs;
	bs.set(4);
	bs.set(6);
	bs.set(2);
	cout << bs.size() << endl;//16
	cout << bs << endl;//0000000001010100
	//获取指定位的状态
	cout << bs.test(0) << endl;//0
	cout << bs.test(2) << endl;//1
	//反转所有位
	bs.flip();
	cout << bs << endl;//1111111110101011
	//反转第1位
	bs.flip(1);
	cout << bs << endl;//1111111110101001
	cout << bs.count() << endl;//12
	//清空第3位
	bs.reset(3);
	cout << bs << endl;//1111111110100001
	//清空所有位
	bs.reset();
	cout << bs.none() << endl;//1
	cout << bs.any() << endl;//0
	//设置所有位
	bs.set();
	cout << bs.all() << endl;//1
	return 0;
}

注意: 使用成员函数set、reset、flip时,若指定了某一位则操作该位,若未指定位则操作所有位。


3.bitset运算符的使用

如表格所示:

运算符功能说明
>>、<<输入输出运算符
=赋值运算符
==、!=关系运算符
&=、|=、^=、<<=、>>=复合赋值运算符
~单目运算符
&、|、^位运算符
[ ]operator[ ]运算符

示例:

int main()
{
	//>>输入、<<输出运算符
	bitset<8> bs;
	cin >> bs;//10100
	cout << bs << endl;//00010100
	//复合赋值运算符
	bitset<8> bs1("101011");
	bitset<8> bs2("100100");
	cout << (bs1 >>= 2) << endl;//00001010
	cout << (bs2 |= bs1) << endl;//00101110
	//位运算符
	bitset<8> bs3("10010");
	bitset<8> bs4("11001");
	cout << (bs3 & bs4) << endl;//00010000
	cout << (bs3 ^ bs4) << endl;//00001011
	//operator[]运算符
	cout << bs3[4] << endl;//1
	cout << bs3[2] << endl;//0
}

三、bitset位图的模拟实现

1.位图的基本框架

  • bitset能实现对数字的位的操作,同时也能通过类似于数组的下标来访问各个位的数值,以执行相应的操作。模拟bitset就是用一个普通的数组来存储数据以达到模拟的目的。
  • 如果我们以一个整型作为比特位的容器,那么如果要求0~N范围的比特位,就需要有N/32+1 个整型来容纳这些比特位,同理如果以char为容器,则需要N/8+1个char来容纳N个比特位。这里我们用vector数组作为底层容纳比特位的容器,且其存储的数据类型为char。
namespace bitset_realize
{
	//N个比特位的位图
	template<size_t N>
	class bitset
	{
	public:
		//构造函数
		bitset();
		//把x映射的位标记成1
		void set(size_t x);
		//把x映射的位标记成0
		void reset(size_t x);
		//判断指定比特位x的状态是否为1
		bool test(size_t x);
		//翻转指定pos
		void flip(size_t x);
		//获取位图中可以容纳位N的个数
		size_t size()
		//统计set中1的位数
		size_t count();
		//判断所有比特位若无置为1,返回true
		bool none();
		//判断位图中是否有位被置为1,若有则返回true
		bool any();
		//全部NUM个bit位被set返回true
		bool all();
	private:
		vector<char> _bits;//位图
	};
}

2.成员函数

2.1.构造函数

  • 一个char类型有8个bit位,所以理想状态下N个比特位的位图就需要用到N / 8个字节,但仅限于N是8的整数倍,如果N位10,那么计算下来就会少2个比特位,因此综合考虑,我们给出N / 8 + 1个字节,这样算下来,所需的N个比特位绝对都能访问到,最多可以整除的情况下浪费了8个比特位(1字节)

而构造函数,我们只需要对这所有的比特位(N / 8 + 1)个字节的大小初始化为0即可。

//构造函数
bitset()
{
	//+1保证足够比特位,最多浪费8个比特位
	_bits.resize(N / 8 + 1, 0);
}

2.2.set reset test

  • 1、set

set的作用是把x映射的位置标记成1,实现规则如下:

  1. 通过x / 8计算x在第i个char类型
  2. 通过x % 8计算x在char第j个比特位
  3. 利用按位或 | 把第i个char中的第j个比特位置为1

image-20230413150034584

//把x映射的位标记成1
void set(size_t x)
{
	//x映射的比特位在第几个char对象
	size_t i = x / 8;
	//x在char第几个比特位
	size_t j = x % 8;
	//利用按位或|把第j位标记成1
	_bits[i] |= (1 << j);
}
  • 2、reset

reset的作用是把把x映射的位标记成0,实现规则如下:

  1. 通过x / 8计算x在第i个char类型
  2. 通过x % 8计算x在char第j个比特位
  3. 将1左移 j 位再整体反转后与第 i 个char类型进行与运算即可。

image-20230413150654029

//把x映射的位标记成0
void reset(size_t x)
{
	//x映射的比特位在第几个char对象
	size_t i = x / 8;
	//x在char第几个比特位
	size_t j = x % 8;
	//将1左移 j 位再整体反转后与第 i 个char进行与运算
	_bits[i] &= (~(1 << j));
}
  • 3、test

test的作用是判断指定比特位x的状态是否为1,实现规则如下:

  1. 通过x / 8计算x在第i个char类型
  2. 通过x % 8计算x在char第j个比特位
  3. 将1左移 j 位后与第 i 个char类型进行与运算得出结果
  4. 若结果非0,则该位被设置,否则该位未被设置

image-20230413150940919

//判断指定比特位x的状态是否为1
bool test(size_t x)
{
	//x映射的比特位在第几个char对象
	size_t i = x / 8;
	//x在char第几个比特位
	size_t j = x % 8;
	//将1左移 j 位后与第 i 个char类型进行与运算得出结果
	return _bits[i] & (1 << j);
}

2.3.flip count size

  • 1、flip

flip的作用是用于翻转指定位,若指定位为0,翻转后为1,若指定位为1,反转后为0,实现规则如下:

  1. 通过x / 8计算x在第i个char类型
  2. 通过x % 8计算x在char第j个比特位
  3. 将1左移 j 位后与第 i 个char进行按位异或运算^即可。

image-20230413151651923

//翻转指定位x
void flip(size_t x)
{
	//x映射的比特位在第几个char对象
	size_t i = x / 8;
	//x在char第几个比特位
	size_t j = x % 8;
	//将1左移 j 位后与第 i 个char进行按位异或运算^即可。
	_bits[i] ^= (1 << j);
}
  • 2、count

count的作用是统计位图中被设计为1的个数,实现规则如下:

  1. n = n & (n-1) => 消去n的二进制数中最右边的1
  2. n不为零继续执行第一步
  3. 执行了几次说明n中有多少个1

image-20230413152942235

//统计set中1的个数
size_t count()
{
	size_t count = 0;
	for (auto e : _bits)
	{
		int n = e;
		while (n)
		{
			n = n & (n - 1);
			count++;
		}
	}
	return count;
}
  • 3、size

size的作用是获取位图中可以容纳位N的个数

//获取位图中可以容纳位N的个数
size_t size()
{
	return N;
}

2.4.none any all

  • 1、none

none的作用是遍历每一个char,如果全为0,则none返回true。

//判断所有比特位若无置为1,返回true
bool none()
{
	//遍历每个char
	for (auto e : _bits)
	{
		if (e != 0)//说明有位被置为1,返回false
			return false;
	}
	return true;//说明全为0,返回true
}
  • 2、any

any的作用判断位图中是否有位被置为1,若有则返回true,这其实和none的作用刚好相反,因此我们直接复用none即可。

//判断位图中是否有位被置为1,若有则返回true
bool any()
{
	return !none();
}
  • 3、all

all的作用是判断位图中是否所有的位都被置为1,注意这里的特殊性,先前开辟空间时我们为了避免出现N/8不能整除的情况,特地在resize时多开了一个char类型(8比特)的空间,这8比特里面只有前N%8个比特位才是有效的(剩下的都是多开的空间),因此all函数需要分情况讨论。

  1. 先检查前n-1个char的二进制是否为全1。
  2. 再检查最后一个char的前N%8个比特位是否为全1。

image-20230413155421403

//全部NUM个bit位被set返回true
bool all()
{
	size_t size = _bits.size();
	//先检查前N-1个char
	for (size_t i = 0; i < size - 1; i++)
	{
		if (~_bits[i] != 0)//取反应该为0,否则取反之前不全为1,返回false
			return false;
	}
	//再检查最后一个char的前 N%8 个位
	for (size_t j = 0; j < N % 8; j++)
	{
		if ((_bits[size - 1] & (1 << j)) == 0)//和test的原理一致
			return false;
	}
	return true;
}

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

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

相关文章

【Linux】进程学习(1)---理解进程概念

文章目录 冯诺依曼体系结构理解冯诺依曼体系结构 操作系统概念与定位概念计算机管理模型计算机的软硬件体系结构图系统调用和库函数概念 进程基本概念描述进程--PCBtask_struct内容分类组织进程 冯诺依曼体系结构 数学家冯诺依曼提出了计算机制造的三个基本原则&#xff0c;即采…

gnome换回纵向切换工作区

效果&#xff1a; 思路 最新的debian / ubuntu中用的gnome 4.x&#xff0c;工作区切换变成了左右切换&#xff0c;习惯了上下&#xff0c;真的很不舒服。 而且优化选项里也把设置开关取消掉了&#xff0c;解决方案是使用Vertical overview这个扩展&#xff1a; ## 安装扩展管…

5.1 大数定律

我的理解&#xff1a; 大数定律的基本思想是&#xff1a;当我们有足够的样本数量时&#xff0c;样本的平均值可以相对准确地估计总体的平均值。这是因为随着样本数量的增加&#xff0c;样本平均值的波动会逐渐减小&#xff0c;从而趋向于总体平均值。 我们可以通过一个简单的…

rk3568-rk809电池电量计

简介&#xff1a; RK809 集成在RK3568上的一个高性能的 PMIC&#xff08;(Power Management IC):电源管理集成电路&#xff09;&#xff0c;PMIC全称Power management integrated circuit&#xff0c;一般情况下是一颗独立于主控的芯片&#xff0c;集成了电源控制&#xff0c;电…

Docker源码阅读总结

Docker总架构图 客户端-服务器架构以及请求的发送&#xff0c;解析等原理不再赘述&#xff0c;这不是我们学习docker的重点。我们知道&#xff0c;Docker提供给了我们一个在隔离环境中运行的进程&#xff0c;那么我其实想深入探究的是 容器的网络是怎么在这个隔离的环境中与其…

【花雕学AI】如何避免ChatGPT封号,安全稳定地使用ChatGPT的技巧和简单过渡办法

虽然ChatGPT 目前已经不是测试期了&#xff0c;它在 2021 年 11 月正式推出&#xff0c;并且在 2021 年和 2022 年后进行了多次更新&#xff0c;提升了真实性和数学能力等方面。但是&#xff0c;ChatGPT 的运算压力仍然很大&#xff0c;因为它需要处理海量的数据和请求&#xf…

ASUS ZenBook Duo 14 UX481电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件型号驱动情况 主板ASUS ZenBook Duo 14 UX481 处理器• Intel Core i7-10510U • Intel Core i5-10210U 已驱动 内存Internal 4GB 2666 Changeable 8GB 2666 mem…

【WCH】CH32F203基于硬件I2C + SSD1306 OLED跑图形库

【WCH】CH32F203基于硬件I2C SSD1306 OLED跑图形库 &#x1f388;基于STM32图形库开源项目地址&#xff1a;https://github.com/hello-myj/stm32_oled&#x1f4cc;相关篇《【WCH】CH32F203硬件I2C驱动SSD1306 OLED》&#x1f4cd;《【WCH】CH32F203软件I2C驱动SSD1306 OLED》…

CountDownLatch背后的原理

前言&#xff1a; 在日常工作中常用到多线程&#xff0c;如果使用多线程处理那么就要考虑同步问题&#xff0c;一般我们会考虑使用加锁来解决。但是还有一些场景&#xff0c;如下&#xff1a; 场景&#xff1a;小升初考试&#xff0c;考生做题&#xff0c;监考老师要等待所有考…

论文阅读【2】-SepViT: Separable Vision Transformer论文结构漫谈与Python实现测试

可分离卷积ViT实现轻量级transformer结构1. 论文主要工作1.1 摘要内容1.2 写作动机&#xff08;Motivations&#xff09;1.2.1 Transformer Patch结构的巨大计算量问题1.2.2 Swin&#xff1a;针对计算量的优化1.2.3 Twins&#xff1a;针对边缘端部署优化1.2.4 Cswin&#xff1a…

【c#串口通信(2)】串口相关参数介绍

1、端口号&#xff08;Port&#xff09; 我们使用一个串口的时候&#xff0c;首先是要打开这个串口&#xff0c;那么我们怎么知道电脑上现在支持几个串口呢&#xff1f;对应的端口号又是什么呢&#xff1f; 由于我的电脑系统是window11&#xff0c;下面就以window11为例介绍如…

部分国产水文水动力模型介绍

一、HydroMPM模型 1、模型介绍 2016年度自立项目HydroMPM系统开发与集成完成的洪水分析模拟软件等成果经权威专家鉴定整体达到国际领先水平&#xff0c;HydroMPM_FloodRisk入选国家防总《全国重点地区洪水风险图编制项目可选软件名录》。成果应用项目100余项&#xff0c;累计…

spring自定义命名空间

命名空间 如果你曾经在配置datasource是用过properties文件储存我们的数据库连接信息&#xff0c;那么一定在xml文件中配置过这样的语句。 <context:property-placeholder location"classpath:jdbc.properties"/>而我们的spring当中很明显是没有这个context的…

【git】git的一些基础操作

文章目录一.git下载二.git初次操作1.生成公钥2.修改全局用户名和邮箱地址&#xff1a;3.本地仓库关联远端仓库4.本地初始化5.将项目上所有的文件添加到本地仓库6.提交到本地仓库7.创建main分支8.推送到main分支三.git其他操作1.develop分支2.查看分支3.切换分支4.查看分支历史一…

python wannier90 基于wannier90的*_hr.dat文件选取截断hopping绘制能带图

我们知道wannier90可以根据选取TMDs的轨道信息生成详细的hopping energy *_hr.dat文件&#xff0c;选取所有的hopping绘制起来的时候比较简单&#xff0c;但是我们发现取几圈的近似hopping也可以将band表示出来&#xff0c;类似的思想有Pybinding的三带近似&#xff08;DOI: 10…

区块链技术在软件开发中的应用

如果你是一名软件开发者或者IT从业者&#xff0c;你一定已经听说过区块链技术。区块链是一种基于密码学的分布式账本技术&#xff0c;被广泛应用于数字货币、金融、物联网等领域。但是&#xff0c;除了这些领域之外&#xff0c;区块链技术还可以在软件开发中发挥重要作用。本文…

CLIP 论文解读

文章目录模型训练推理实验与Visual N-Grams 相比较分布Shift的鲁棒性不足参考现有的计算机视觉系统用来预测一组固定的预订对象类别&#xff0c;比如ImageNet数据集有1000类&#xff0c;CoCo数据集有80类。这种受限的监督形式限制了模型的通用性和可用性。使用这种方法训练好的…

《花雕学AI》02:人工智能挺麻利,十分钟就为我写了一篇长长的故事

ChatGPT最近火爆全网&#xff0c;上线短短两个多月&#xff0c;活跃用户就过亿了&#xff0c;刷新了历史最火应用记录&#xff0c;网上几乎每天也都是ChatGPT各种消息。国内用户由于无法直接访问ChatGPT&#xff0c;所以大部分用户都无缘体验。不过呢&#xff0c;前段时间微软正…

Nginx实现会话保持,集群模式下session域共享

前言 生产环境下&#xff0c;多数系统为了应对线上多种复杂情况而进行了集群架构的部署&#xff0c;保证系统的高性能、价格有效性、可伸缩性、高可用性等。通常将生产环境下的域名指向Nginx服务&#xff0c;通过它做HTTP协议的Web负载均衡。 session是什么 在计算机中&…

13.广度优先搜索

一、算法内容 1.简介 广度优先搜索BFS&#xff08;Breadth First Search&#xff09;按照广度优先的方式进行搜索&#xff0c;可以理解为“尝试所有下一步可能”地穷举所有可行的方案&#xff0c;并不断尝试&#xff0c;直到找到一种情况满足问题问题的要求。 BFS从起点开始…