C++位图

news/2024/11/22 8:49:43/

位图

位图

文章目录

  • 位图
      • set
      • Reset
      • Test
      • 整体代码
      • 位图应用

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

首先想到的是归并排序+二分查找。排序可以排,但是通过文件指针去查找会很慢。

其次是set和哈希表。set自动可以排序且在红黑树中查找速度也很快。但要把40亿个整数加上红黑树的节点(三叉链外加颜色)放进内存里,内存明显不够,不可取;哈希表同样是把40亿个整数外加节点放进内存里,内存明显不够,也不可取。

那么既然要把40亿个整形放进内存里,判断在或者不在,用1标记在用0标记不在。1个比特位就能满足标记1或0。用直接定址法。1个char类型-1个字节-8个比特位。无符号整数有42亿9千万个,全部用比特位来代表的话就只需要512M。这种方法可行。用char类型来开辟空间,那么第一个char就能存储07,第二个char就能存储815,第三个。。。。。。

image-20230422112358980

set

把要set的值通过/8找到对应的char(小位图),再通过%8找到对应的位置,把该位置标记成1即为该值存在于这堆数中

		void Set(size_t x)//把x值对应的标记为置为1{//计算x位于哪一个char上//	size_t i = x / 8;size_t i=x >> 3;//相当于x/8//计算x位于哪个bit上size_t j = x % 8;_bit[i] |= (1 << j);}

位图set

Reset

把要Reset的值通过/8找到对应的char(小位图),再通过%8找到对应的位置,把该位置标记成0即把该值从这堆数中抹去

void ReSet(size_t x)//把x值对应的标记置为0{//计算x位于哪一个char上//size_t i = x / 8;size_t i = x >> 3;//相当于x/8//计算x位于哪个bit上size_t j = x % 8;_bit[i] &= (~(1 << j));}

位图reset

Test

把要Reset的值通过/8找到对应的char(小位图),再通过%8找到对应的位置。先按位取反原来的位图,再把原来的位图与取反的位图按位与,若存在1则为非0,为真返回true;若不存在则没有1全0,为假,返回false;

		bool Test(size_t x)//判断x是否在这堆数里面{//计算x位于哪一个char上//size_t i = x / 8;size_t i = x >> 3;//相当于x/8//计算x位于哪个bit上size_t j = x % 8;return _bit[i] & (1 << j);}

位图test1

位图test2

整体代码

	template<size_t N>//用非类型模板参数---N为要往位图里存储多少个数class  BitSet{public:BitSet(){//_bit.resize(N >> 3) + 1);_bit.resize(N / 8 + 1);//多开一个}void Set(size_t x)//把x值对应的标记为置为1{//计算x位于哪一个char上//	size_t i = x / 8;size_t i=x >> 3;//相当于x/8//计算x位于哪个bit上size_t j = x % 8;_bit[i] |= (1 << j);}void ReSet(size_t x)//把x值对应的标记置为0{//计算x位于哪一个char上//size_t i = x / 8;size_t i = x >> 3;//相当于x/8//计算x位于哪个bit上size_t j = x % 8;_bit[i] &= (~(1 << j));}bool Test(size_t x)//判断x是否在这堆数里面{//计算x位于哪一个char上//size_t i = x / 8;size_t i = x >> 3;//相当于x/8//计算x位于哪个bit上size_t j = x % 8;return _bit[i] & (1 << j);}vector<char> _bit;};

当开最大的整形数时,内存也只占512M左右

image-20230423160349779

库里面也有

bitset
image-20230423160904693

位图应用

  1. 快速查找某个数据是否在一个集合中
  2. 排序+去重
  3. 求两个集合的交集、并集等
  4. 操作系统中磁盘块标记
给两个文件,分别有100亿个整数,我只有1G内存,如何找到两个文件的交集???

把文件1的数据放进位图1,把文件2的数据放进位图2,然后逐个遍历位图1的数据同时遍历位图2。当两个位图的数据的标记位都是1时,说明该数据即存在文件1也存在文件2,这个数据就是两个文件的交集。逐个遍历两个位图,找出相同的数据即可。

image-20230423183904153

//测试
void testBitset(){BitSet<100> bs1;BitSet<100> bs2;int path1[] = { 1,2,3,4,5 };int path2[] = { 1,3,5 };for (auto e : path1){bs1.Set(e);}for (auto e : path2){bs2.Set(e);}for (size_t i = 0; i < 100; i++){if (bs1.Test(i) && bs2.Test(i))//11{cout << i << endl;}}}
一个文件有100亿个int,1G内存,设计算法找出次数不超过2次的所有整数

用两个位图来记录出现的数据次数。出现0次就是00,出现1次就是01,出现2次就是10,出现3次或以上就是11。记录两个位图标记位为01,10的数据。

浅搓了个代码

template<size_t N>class twoBitset{public:void Set(size_t x){if (!bs1.Test(x) && !bs2.Test(x))//两个位图都是0---数据出现0次{//00->01bs2.Set(x);}else if (!bs1.Test(x) && bs2.Test(x))//第一个位图是1,第二个位图是0---数据出现1次{//01->10bs1.Set(x);bs2.ReSet(x);}else //两个位图都是1---数据出现2次{//10->11bs2.Set(x);}//else//出现3次及以上//{//	break;//}}void  Printones(){for (size_t i = 0; i < N; i++){if (!bs1.Test(i) && bs2.Test(i))//01{cout << i << endl;}else if (bs1.Test(i) && !bs2.Test(i))//10{cout  << i << endl;}}}private:BitSet<N> bs1;BitSet<N> bs2;};
给一个超过100G的log file,log中存着IP地址,设计算法找到出现次数最多的IP地址

先通过哈希函数哈希切分这个100G的文件,然后冲突的IP地址放进同一个小文件里;接着用map依次统计每个文件的相同IP的次数,统计完一个clear掉map统计下一个。

此时小文件有两种情况,一是小文件里大部分冲突的IP都是重复的,此时直接用map可以统计次数。二是小文件里大部分冲突的IP都是不重复的,此时用map统计不下,使用mp的insert时会插入失败,即没内存去new节点了,new失败会抛异常,这时需要换个哈希函数,对这个小文件再次通过哈希切分,分成更细小的文件。

image-20230423213026093


http://www.ppmy.cn/news/49339.html

相关文章

校招面试重点汇总之多线程(不多但都是高频面试题)

一、进程和线程有什么区别&#xff1f; 进程和线程都是操作系统中用来实现多任务的概念。但是它们之间有一些重要的区别&#xff0c;如下所述&#xff1a; &#xff08;1&#xff09;定义方面 进程&#xff1a;进程是操作系统中分配资源的基本单位&#xff0c;是正在运行中的一…

18 标准模板库STL之deque

基础知识 1、deque是一个双端数组容器,可以同时在头部和尾部添加、移除元素。deque与vector类似,也支持随机访问,但vector是一整段的连续内存空间,而deque是一段一段的连续内存空间。每一段连续内存空间称为一个deque块,所有deque块由一个map进行管理。 2、deque在头部和尾…

「C/C++」C/C++异常处理

博客主页&#xff1a;何曾参静谧的博客 文章专栏&#xff1a;「C/C」C/C学习 目录 相关术语一、C语言中的异常处理1.返回值来传递错误信息2.使用标准库函数对异常进行处理&#xff08;不推荐&#xff09;3.使用全局变量来记录错误信息(不推荐) 二、C中的异常处理1.try{}catch()…

【数据结构实验】约瑟夫环

数据结构实验—约瑟夫环 简介 分别利用线性表的顺序存储形式和链式存储形式&#xff0c;按照出列的顺序印出各人的编号。文末贴出了源代码 需求分析 每个人的数据m需要随机产生&#xff0c;可正可负且不是实现存储的&#xff0c;这意味着需要使用随机函数来生成每个人的数据…

redis设计与实现读书笔记(2)

今天看的是关于单机数据库&#xff0c;RDB持久化以及AOF持久化的内容。 关于单机数据库 1.默认数据库数量 redis的服务器默认是会创建16个数据库&#xff0c;每个客户端访问的时候都要指定自己的目标数据库。 select可以切换目标数据库。 注意事项 到目前为止&#xff0c…

瓷器:伟大发明的特点和能量的作用

文章目录 引言I 瓷器的独特性1.1 用途特别大。1.2 影响力广泛,经济意义大1.3 难以替代性。1.4 瓷器的发明特别难II 上釉2.1 上釉方法2.2 发明和发现的区别III 发明的必然性和偶然性3.1 偶然性3.2 偶然性的背后常常有着必然性。3.3 一种技术会抑制另一种技术引言 从预先要求和…

数据库方言:了解不同数据库系统的特性和差异

摘要&#xff1a; 数据库方言指的是不同数据库系统在 SQL 语法和实现上的差异。本文将探讨数据库方言的概念、为什么会存在方言、常见数据库方言的特点以及如何处理方言差异。 1. 什么是数据库方言&#xff1f; 数据库方言是指不同数据库系统在 SQL 语法、数据类型、函数和存…

软考 - IP地址与网络划分

一.IP组成 1.1 首个八位字节规则 1.2 地址掩码 IP地址掩码 标准地址掩码 A类&#xff1a;255.0.0.0 前1个字节是网络号 后3个字节是主机号 B类&#xff1a;255.255.0.0 前2个字节是网络号 后2个字节是主机号 C类&#xff1b;255.255.255.0 前3个字节是网络号 后1个字节是主机号…