【数据结构】哈希应用-STL-位图
目录
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内存,如何找到两个文件的交集?
把数据读出来,分别放到两个位图,依次遍历,同时在两个位图的值就是交集
相关文章:
【数据结构】哈希应用-STL-位图
目录 1、位图的概念 2、位图的设计与实现 2.1 set 2.2 reset 2.3 test 3、C库中的位图 4、位图的优缺点 5、位图相关题目 1、位图的概念 面试题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这4…...
Unbuntu 服务器- Anaconda安装激活 + GPU配置
一、Anaconda安装激活 1.更新 sudo apt-get update 2.安装wget、vim sudo apt-get install wget sudo apt-get install vim 3.安装Anaconda 进入这个网址:Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 点这里&#x…...
python 装饰器记录函数用时
装饰器 # 用于记录函数平均用时的装饰器 def average_time_decorator(func):times []def wrapper(*args, **kwargs):start_time time.time()result func(*args, **kwargs)end_time time.time()t end_time - start_timetimes.append(t) # 记录用时print(f"{func.__n…...
实验10 任何一个非0自然数m的立方均可写成m个连续奇数之和。
实验10 题目描述 任何一个非0自然数m的立方均可写成m个连续奇数之和。 例如: 1^3 1 2^3 35 3^3 7911 4^3 13151719 编程实现:输入一自然数n,求组成心的n个连续奇数。 【实验要求】 1、不允许用等差数列的方法求首项 2、要求使用双重循环&a…...
Jenkins的安装方式
一、Jenkins是什么 Jenkins是一款开源CI&CD软件,用于自动化构建、测试和部署软件等各种任务,以实现持续集成。 Jenkins支持各种运行方式,可通过系统包、Docker或者通过一个独立的Java程序。 二、安装方式 2.1禅道智能应用平台一键安装…...
网络之华为S5700S-52P-LI交换机系统恢复
一、需求说明 盒式交换机flash存储空间一般比较小,只有几百兆,部分比较可能不到100M。当然一般情况下也是够用的,只有在日志文件等占用较多,或者ios系统升级较多,bin文件占用较多的情况下可能出现不够用的情况。什么情…...
蜂窝网络架构
2G/3G 4G eNB RF-RRU eCPRI RRU-BBU 光纤 5G From 38.300 AMF处理信令等,UPF 用户面,后面还有SMF...
培训第二十二天(mysql数据库主从搭建)
上午 1、为mysql添加开机启动chkconfig [rootmysql1 ~]# chkconfig --list //列出系统服务在不同运行级别下的启动状态注:该输出结果只显示 SysV 服务,并不包含原生 systemd 服务。SysV 配置数据可能被原生 systemd 配置覆盖。 要列出 systemd 服务…...
速盾:CDN回源失败都有什么原因?
CDN(内容分发网络)是一种通过将内容分发到全球各个边缘节点来提高网站访问速度和用户体验的网络技术。CDN回源失败是指CDN节点无法正常获取源站(原始服务器)上的内容。下面是一些可能导致CDN回源失败的常见原因: 网络故…...
C语言 | Leetcode C语言题解之第328题奇偶链表
题目: 题解: struct ListNode* oddEvenList(struct ListNode* head) {if (head NULL) {return head;}struct ListNode* evenHead head->next;struct ListNode* odd head;struct ListNode* even evenHead;while (even ! NULL && even->…...
8月6日笔记
8月6日 红日靶场打靶继续 SHOW VARIABLES #用于显示服务器运行时的各种系统变量的当前设置。这些变量可以控制服务器的行为在 MySQL 中,general_log 和 general_log_file 是两个与“general”相关的系统变量,它们控制着服务器是否启用一般查询日志以及…...
爱可声助听器:在全球听力市场中破冰前行
早在2021年,全球助听器市场规模就已经达到了101亿美元,Grand View Research数据显示,这一规模会持续增大,在未来的6年间,该数据将以4.9%的复合年增长率(CAGR)增长。 作为发展中国家,…...
华为OD面试 - 最佳升级时间窗(Java JS Python C C++)
题目描述 有一套系统需升级,为减小系统升级期间的影响,需根据系统过去一段时间内的每小时平均访问数据,来预测最佳升级时间窗。 现给长度为168(7 * 24)的整数数组,表示一个周期(假设从周一00:00到周日24:00)的每小时历史数据,最佳升级时间窗选择规则如下: 时间窗内…...
LE-50821F/FA激光扫描传感器|360°避障雷达之性能参数与配置清单说明
LE系列激光扫描传感器|360避障雷达涵盖LE-50711、LE-50711F、 LE-50621、LE-50821F、LE-50621F、LE-50821FA、LE-50711FA、LE-50621FA等型号,广泛应用于自动化工厂、物流与仓储、汽车制造与物流、机械设备、能源与环境等领域的环境感知、高精度定位(…...
精准洞察农田生态,智慧农业物联网环境监测与数据采集系统来袭
随着智慧农业的快速发展,利用物联网技术实现对农田种植状态的精准监测变得愈发重要。为了确保监测的准确性、一致性和有效性,规范农田物联网监测设备的技术参数、部署安装以及数据对接等技术指标势在必行。 本文技术说明旨在为相关设备的选择、安装和集…...
sql注入复现(1-14关)
目录 第一关(字符型注入) 第二关(数字型注入) 第三关(闭合方式不同) 第四关(用双引号闭合) 第五关(不会数据回显) 第六关(闭合方式不同双引…...
Spring Boot-12
JavaConfig 是一种通过 Java 代码来配置 Spring 应用程序的方式,取代了传统的 XML 配置文件。这 什么是 JavaConfig JavaConfig 是 Spring Framework 的一部分,它允许你使用纯 Java 代码来定义 Spring Beans 和配置应用程序,而不需要 XML 配…...
【Linux】进程详解
1、定义 使用编译器将代码编译成的可执行文件称为程序,程序存储在磁盘上; 将程序从磁盘装载到内存中,并通过指令调用、各级缓存、寄存器运行起来的实例,称为进程; 一个程序可以同时运行多个进程;每个进程具有自己的内存空间、寄存器和文件描述符等资源。 进程ID:…...
python的多线程
python的threading模块,它提供了丰富的接口来创建和管理线程。 定义一个函数print_numbers,这个函数将由线程执行。在这个函数中,我们使用一个循环来打印数字,并使用time.sleep(1)来模拟每个数字打印之间有1秒的延迟。 在 if __…...
在Kylin服务器安装PostgreSQL16数据库
1、下载PostgreSQL16安装包 下载地址https://www.postgresql.org/ftp/source/v16.3/ 2、安装依赖和ICU库 查看服务器版本 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel python-devel gcc-c opens…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
