Learning C++ No.26 【深入学习位图】

news2024/11/24 4:41:32

引言:

北京时间:2023/5/30/15:30,刚睡醒,两点的闹钟,硬是睡到了2点40,那种睡不醒的感觉,真的很难受,但是没办法,欠的课越来越多,压的我喘不过气了都,早上把有关unordered_set和unordered_map的内容给写完了,所以哈希表有关代码方面的知识,我们就搞定的差不多了,并且现在外面高温异常,着实比较恐怖,下午四点还要去进行一场有关计算机导论的考试,目前丝毫没有复习,但我也丝毫不慌张,导论这种课现在给我的感觉,就有点像是……具体不好比喻,反正给我的感觉不怎么好,也许以后等我学习的更加深入时,对于这种课程的感觉就会更加清晰吧!并且还是那句话,60分就好,所以咱不慌,哈哈哈!所以今天我们就接着哈希表相关知识,来看看什么是位图结构,虽然之前学习文件操作时学习过,但只是浅浅的了解了一下,这次我们是深入学习位图结构哦!
在这里插入图片描述

图是什么

图给我的第一印象

自从我们接触数据结构开始,我们就知道数据结构中有线性结构、非线性结构,并且知道在学习完线性结构相关的容器之后,就有两座大山,也就是有关树状结构和图状结构,当时在还没有学习什么红黑树、AVL树等容器时,树状结构和图在我的感知中都是较为复杂的数据结构,并且因为图在树之后学习,让我认为图比树的学习难度还更大,所以这就是图给我带来的第一印象,今天我们就来看看图,到底有没有想象中的那么复杂

到底什么是图

经过我的一番学习之后,我意识到了,在数据结构中图并不是我们生过中理解的地图或者纸图,而是位图,那么什么是位图呢?
简简单单,想要快速认识位图,我们从一个经典的面试题目进行分析,如下:

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

首先,看到这种有关查找某个元素的问题,不就是去调用查找接口就可以搞定吗?那么像vector、list这些容器不都有查找接口吗?此时只需要将对应的数据插入到该容器中,然后不就可以调用该容器对应的查找接口去查找该元素是否存在于这些数据之中了吗?当然有的同学会认为,这样的查找方式效率非常的低,可以改进,将数据进行排序,排完序之后,使用二分查找法,在O(logN)的时间复杂就可以轻轻松松搞定,又快又好,或者是使用查找效率更高的数据结构,如红黑树或哈希表,这样就可以将查找效率大大提高啦!这个问题不是很简单吗?但是此时由于数据量是40亿的原因,所以上述方法并不适用,主要涉及到了存储相关的知识,数据量大越大,存储就越困难,并且明白,我们电脑的内存一般是4G~16G,并且这其中还包括了操作系统等软硬件使用的那部分,所以真正能够提供给我们使用的内存大约只有12G到15G,然而,此时如果是40亿个整形数据的话,单单是这方面的数据就需要16G的内存,还没有包括像红黑树中每个结点需要有一个指针,否则就又需要16G,哈希表同理,有而外指针需要存储,所以使用哈希表和红黑树来处理这个问题非常的不明智

充分认识位图

所以想要解决这个问题,那么此时最好的方法就是使用位图结构,原因就是,位图是一种用比特位来表示数据集合的数据结构,它将一个整数集合视为一个由0或1组成的二进制序列,并使用其中每一个比特位来代表对应被存储的整形数据 ,表示的意思就是我们可以使用比特位来标示每一个整形数据,例:此时某一个整数集合{1,2,3,4,5},某一个位图结构00000000,如果对应的整数集合中存在1,我们就把位图结构中的某个比特位由0置1,00000001,如果存在2,那么同理将位图结构中的某个比特位由0置成1,00000011,以此类推,最终把整数集合中的所有数据通过比特位的形式标示出来,这样我们就极大的节省了空间,假如还是40亿个整形数据,那么就不需要16G内存=160亿字节=1280亿个比特位,而只需要40亿个比特位=5亿字节=0.5G内存就可以将40亿个整形数据存储到内存啦!

位图结构代码实现

所以上述就是有关位图结构的基本认识,也就是今天我们学习的重点,文字描述起来,比较抽象,如果对于新手来收,那么想要理解,还是有一定成本的,所以为了更好的认识位图结构,此时我们就来看看如何使用代码来实现位图结构吧!如下:

1.位图基本模型
在这里插入图片描述
2.位图结构代码实现
根据上述的基本模型,此时我们对位图结构就有了更深一步的认识,此时我们再通过代码实现,将该结构给搞定吧!当然我们只是简单实现位图中最关键的接口,如下:

将对应位置由0置1
在这里插入图片描述
将对应位置由1置0
在这里插入图片描述
判断该数据是否存在
当然也就是判断该数据对应的比特位是1还是0,判断方法如下图所示:
在这里插入图片描述

位图结构有关变形问题

1.在100亿个数据中找只出现一次的数据

本质还是在利用上述位图结构有关的知识,只不过此时使用的是两个位图结构,其中一个位图结构用于记录第一次出现的数据,另一个位图结构用于记录第二次出现的数据,具体代码如下图所示:
在这里插入图片描述

2.两个有100亿整数的文件,只有1G内存,如何找到两个文件的交集

最优思路读取文件1的数据映射到位图1,读取文件2的数据映射到位图2,如果两个位置都是1就说明是交集,反之不是交集(注意:每个位图的位置代表的都是不同的值)

总结:位图有关知识就是用来解决类似上述这种海量数据处理问题,使用位图结构就是在使用最少的空间,去存储最多的数据

上述完整代码如下:

#pragma once
//明白计算机的移位,无论如何都是1左移几位,然后到达第几位 00000001左移几位得到0000x000
//左移和右移的本质一定是低位到高位(左移)或者高位到低位(右移)

#include<iostream>
#include<vector>

using namespace std;

//1.40亿个数据中,找某一个数据

template<size_t N>//这样子的模板参数需要注意
class bitmap
{
public:
	bitmap()
	{
		_bits.resize(N / 8 + 1, 0);//这样子去开比特位会导致取整时比特位丢失(并且注意:此时N代表的是N个比特位,所以此时resize代表的是开辟多少个字节) 
	}

	void set(size_t x)//将对应的比特位置成0
	{
		size_t i = x / 8;
		size_t j = x % 8;
		//找到对应的位之后,此时就需要将对应的位置成1
		_bits[i] |= (1 << j);//这个不敢看不同,就是数组下标1处的char(8个比特位)去按位或上1左移j为后的二进制,如果j为4,那么也就是按位或上00010000(注意:是从右边开始)
		//也就是 00000000按位或上00010000 最终得到 00010000(并且注意此时是按位或等)

	}

	void reset(size_t x)//将对应的比特位置成1
	{                                                    //     0       1        2        3
		//1.先算是在第几个8比特位(也就是第几个第几个char) 00000000 00000000 00000000 00000000  (注意:需要从右开始计算比特位,因为此时是char类型)
		size_t i = x / 8;//也就是计算x映射的位,在数组位置中的第几个char,如果x为12,那么此时该x的位就在第一个数组下标中
		size_t j = x % 8;//也就是计算x映射的位,在该数组下标char中的第几个位,同理x为12,那么此时x就位于char中的第4个位
		//所以总的来说,此时该x的位置就是第二个char(也就是数组下标为1)中的第四个位
		//所以代码来到这里,就表示我们已经找到对应的位置了
		
		//明白一个点,0跟任何数按位或还是任何数,0跟任意数按位与还是0
		//所以此时我们想要将对应的比特位置0,此时使用的就是按位与(让对应为1的位置和0进行按位与)
		_bits[i] &= ~(1 << j);//同理,00010000按位与上~(00010000)=>11101111,最终得到00000000
	}

	bool test(size_t x)//判断在不在
	{
		size_t i = x / 8;
		size_t j = x % 8;

		return _bits[i] & (1 << j);//本质就是在判断对应的那个位置是1还是0,只要不是0,那么就是真(true)例:00111100 & 00010000 => 00010000 ,虽然不是1,但是肯定不是0,所以返回true,只有当 00101111 & 00010000这种情况,当对应位置为0,那么就会返回false
	    //同理写法_bits[i] >> j & 1;  //但是要注意运算符的优先级
	}

private:
	vector<char> _bits;
};

void test_bitmap()//切记类名哪里都可以使用,需要指明类域的是该类里面的公共成员函数,但是可以直接使用类型构造对象使用
{
	bitmap<100> bm;//此时传的模板参数是100,结合上述代码表示的就是开辟100个比特位的空间(并且注意:此时一个比特位表示的就是存储一个整形数据)
	bm.set(12);//这个表示的就是将12按照比特位的形式表示出来,也就是上述的00010000  -->  16   --> 10
	bm.set(11);//00011000    -->    24    --> 18
	bm.set(15);//10011000    -->    152   --> 98(16进制)

	bm.reset(15);

	cout << bm.test(12) << endl;
	cout << bm.test(15) << endl;

	//如何直接将空间开到最大
	bitmap<-1> bm1;//此时类似之前学习的npos(反正表示的就是一个43亿左右的数),也就是表示此时在对应的位图中可以映射出了43亿个数据

}

//2.位图有关的变形问题(在100亿个数据中找只出现一次的数据)

template<size_t N>
class twobitmap
{
public:
	void set(size_t x)
	{
		if (_bm1.test(x) == false && _bm2.test(x) == false)
		{
			_bm2.set(x);//00->01   //满足该条件,就说明该数据是第一次出现,此时我们只要将该数据映射的位图置成1就行
		}
		else if (_bm1.test(x) == false && _bm2.test(x) == true)
		{
			_bm1.set(x);
			_bm2.reset(x); //01->10   满足该条件,就说明,该数据出现了一次以上,此时就只要将对应的位图由01(只出现了一次),置成10(出现了多次)就行
		}
		else
		{
			//10   多次出现
		}
	}
	void print()//打印只出现一次的值
	{
		for (size_t i = 0; i < N; i++)//遍历43亿个数
		{
			if (_bm2.test(i) == 1)//此时表示的就是01结果(表示该数据只出现了一次)
			{
				cout << i << endl;
			}
		}
	}
private:
	bitmap<N> _bm1;//直接去封装上面的,实现两个不一样的就行,00表示该数据没有出现,01表示该数据只出现了一次,10表示该数据出现多次
	bitmap<N> _bm2;
};
void test_twobitmap()
{
	int arr[] = { 3,6,8,10,45,67,90,5,144,6,1,0,22,222,67,14 };//注意:这个位置给值的时候要小心越界访问
	twobitmap<1000> bm;
	for (auto e : arr)
	{
		bm.set(e);
	}

	bm.print();
}

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

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

相关文章

华为OD机试真题B卷 Java 实现【整理扑克牌】,附详细解题思路

一、题目描述 给定一组数字&#xff0c;表示扑克牌的牌面数字&#xff0c;忽略扑克牌的花色&#xff0c;请按如下规则对这一组扑克牌进行整理&#xff1a; 步骤1 对扑克牌进行分组&#xff0c;形成组合牌&#xff0c;规则如下&#xff1a; 当牌面数字相同张数大于等于4时&a…

【Python Selenium】零基础也能轻松掌握的学习路线与参考资料

Python Selenium是一种基于Python编程语言的自动化测试框架&#xff0c;用于Web应用程序的测试和自动化。Python Selenium是一个非常流行的工具&#xff0c;它可以通过模拟用户行为来测试Web应用程序&#xff0c;同时还可以通过Python编写脚本实现自动化测试&#xff0c;并且可…

Spring Boot如何实现自定义Starter?

Spring Boot如何实现自定义Starter&#xff1f; 在 Spring Boot 中&#xff0c;Starter 是一种特殊的依赖&#xff0c;它可以帮助我们快速地集成一些常用的功能&#xff0c;例如数据库连接、消息队列、Web 框架等。在本文中&#xff0c;我们将介绍如何使用 Spring Boot 实现自…

python视频图片美化

python视频图片美化 git clone https://github.com/s0md3v/roop.git If you aren’t good with following commands, here’s a video tutorial install python (and pip too if neeed) install git install ffmpeg If you are on Windows, install “Microsoft Visual C 14”…

Cadence OrCAD Capture 搜索的到的元器件无法在图纸中找到的问题

🏡《总目录》 目录 1,概述2,问题现象3,问题原因解决方案4,总结B站关注“硬小二”浏览更多演示视频 1,概述 本文简单介绍在使用Capture绘图时,搜索元器件或其他对象,存在搜索结果,但在图纸中无法找到的原因和解决方案。 2,问题现象 如下图所示搜索U20后,存在搜索结果…

javascript基础十二:JavaScript中的事件模型如何理解?

一、事件与事件流 javascript中的事件&#xff0c;可以理解就是在HTML文档或者浏览器中发生的一种交互操作&#xff0c;使得网页具备互动性&#xff0c; 常见的有加载事件、鼠标事件、自定义事件等 由于DOM是一个树结构&#xff0c;如果在父子节点绑定事件时候&#xff0c;当触…

浅析Redis集群数据倾斜问题及解决方法

概 述 在服务端系统服务开发中&#xff0c;缓存是一种常用的技术&#xff0c;它可以提高系统对请求的处理效率&#xff0c;而redis又是缓存技术栈中的一个佼佼者&#xff0c;广泛的应用于各种服务系统中。在大型互联网服务中&#xff0c;每天需要处理的请求和存储的缓存数据…

【Java系列】Mybatis-Plus 使用方式介绍

1 Mybatis-Plus简介 Mybatis-Plus 提供了多种方式来执行 SQL&#xff0c;包括使用注解、XML 映射文件和 Lambda 表达式等。其中&#xff0c;使用 Lambda 表达式是 Mybatis-Plus 推荐的方式&#xff0c;因为它更加直观和类型安全。 2 使用方法 1 Lambda 表达式执行 SQL 以下是…

pix2pixHD---loss---损失函数

在Pix2PixHDModel代码中首先定义损失&#xff1a; 首先看第一个&#xff1a;输入的两个参数use_gan_feat_loss, use_vgg_loss默认为false&#xff0c;则前缀有not&#xff0c;所以两个参数都是True。 def init_loss_filter(self, use_gan_feat_loss, use_vgg_loss):flags (Tr…

PCIE知识点-022:PCIe 参考时钟结构

图1&#xff1a;参考时钟结构示意图[4] 1. Common Refclk Architecture Common Refclk Architecture&#xff0c;即同源参考时钟架构&#xff0c;PCIe收发设备共用一个时钟源&#xff0c;是目前是使用最为广泛的方案。 缺点&#xff1a; 对于适用同一 Common Clock 作为参考时…

第四章 运行时数据区

文章目录 前言一、&#x1f697; 双亲委派机制1、 问题的引出&#xff1a;是否会被外来程序对系统进行破坏2、总结3、双亲委派的优势4、沙箱安全机制5、其他 二、&#x1f692; 运行时数据区线程 三、&#x1f6fa; PC 寄存器概述&#xff08;记录下一条程序指令的地址&#xf…

Vulnhub: dpwwn: 1靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.131 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.131 爆破出mysql的root用户为空密码 hydra -l root -P /usr/share/wordlists/rockyou.txt 192.168.111.131 -s 3306 mysq…

UI自动化 Xpath定位必知必会

目录 javascript xpath定位 定位单个元素&#xff1a; 定位多个元素&#xff1a; 验证xpath定位语法是否OK 尽量使用模糊匹配定位元素 模糊匹配contains 使用关联文本值定位 text() 在UI自动化测试中用的最频繁的就是xpath定位了&#xff0c;所以用好xpath定位至关重要&…

关于中断的几个小问题

1. intel 8259芯片中的IRQ2和int2的区别是什么&#xff1f; 答曰&#xff1a;IRQ2是芯片上的引脚&#xff0c;而int2是中断向量表的第2项&#xff0c;两者有很大区别。 Intel8259A芯片的中断引脚分别为&#xff1a; 主片&#xff1a; 0&#xff1a;8254时钟 1&#xff1a;键盘 …

【python代码】Kittle数据集的ground truth生成深度图攻略|彩色深度图|代码无恼运行

目录 1.明确KITTLE数据集特性 2.选择groundtruth 3.转换深度图 4.转换彩色深度图 1.明确KITTLE数据集特性 KITTI数据集包含了来自车载传感器的多模态数据&#xff0c;包括激光雷达、摄像头和GPS/惯性测量单元&#xff08;IMU&#xff09;等。该数据集主要采集于城市环境中…

B3645 数列前缀和 20

题目描述 给定一个长度为 n 的数列 a&#xff0c;请回答 q 次询问&#xff0c;每次给定 l,r&#xff0c;请求出 ()​mod p 的值&#xff0c;其中 p1,145,141。 输入格式 第一行是两个整数&#xff0c;依次表示数列长度 n 和询问次数 q。 第二行有 n 个整数&#xff0c;第 …

Qt下QTcpServer服务端识别多个QTcpSocket客户端

文章目录 Qt官方文档编写QTcpServerDemo和QTcpSocketDemo实现QTcpServerDemo实现QTcpSocketDemo 使用windeployqt生成程序运行所需依赖文件 Qt官方文档 QTcpSocket Class &#xff1a;https://doc.qt.io/qt-5/qtcpsocket.html QAbstractSocket Class&#xff1a;https://doc.q…

分布式RPC框架Dubbo详解

目录 1.架构演进 1.1 单体架构 1.2 垂直架构 1.3 分布式架构 1.4 SOA架构 1.5 微服务架构 2.RPC框架 2.1 RPC基本概念介绍 2.1.1 RPC协议 2.1.2 RPC框架 2.1.3 RPC与HTTP、TCP/ UDP、Socket的区别 2.1.4 RPC的运行流程 2.1.5 为什么需要RPC 2.2 Dubbo 2.2.1 Dub…

Redis的网络模型(未完成)

Redis是单线程还是多线程&#xff1f; 1)如果只是针对于Redis的核心业务部分(命令处理)&#xff0c;答案是单线程 2)如果是说整个redis&#xff0c;那么就是多线程 在Redis的版本迭代过程中&#xff0c;在两个非常重要的时间节点上引入了对多线程的支持: 1)在Redis4.0版本中&am…

redis第二章-第一课-持久化rdb和aof以及混合模式

RDB快照 1.在默认情况下&#xff0c; Redis 将内存数据库快照保存在名字为 dump.rdb 的二进制文件中&#xff0c;默认存储在redis的当前目录下&#xff0c;也就是和redis.conf同级目录下&#xff0c;可以修改位置 2.你可以对 Redis 进行设置&#xff0c; 让它在“ N 秒内数据…