当前位置: 首页 > article >正文

【数据结构进阶】位图

🌟🌟作者主页:ephemerals__
🌟🌟所属专栏:数据结构

目录

前言

一、位图的概念与结构

二、位图的实现

1. 结构定义

2. 构造函数

3. 三大接口实现

set

unset

test

总代码

4. 测试

三、 标准库的位图

四、位图的优缺点

总结


前言

        在计算机科学中,高效地存储和操作数据是永恒的追求。面对海量数据,传统的存储方式往往显得笨拙而低效,因此,位图作为一种简洁而强大的数据结构,应运而生。它将数据的存在与否抽象为二进制的0和1,利用比特位的排列组合,巧妙地实现了对数据的压缩存储和快速检索。从图像处理到数据库索引,从网络爬虫到布隆过滤器,位图的身影无处不在。本文将带你走进位图的世界,探索其精妙的设计思想和广泛的应用场景。

注:希望大家在学习了哈希表以及位运算的知识后,再来阅读本文,否则其中内容可能难以理解。

一、位图的概念与结构

        首先看一道经典面试题:

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

解决思路:

1. 存放在数组中,按顺序查找。但数据量太大,消耗时间太长。

2. 排序 + 二分查找 (O(NlogN))

3. 使用位图存储数据,达到O(1)查找效率

        那么位图是什么呢?

位图的本质是一个直接定址法的哈希表,其中存储了若干个整形值。每一个整形值都有对应数量的比特位,每一位的二进制值都可以表示两种状态,例如“在”或者“不在”。

使用位图时,将输入的数据进行映射,找到在位图当中的对应位置,该位置的值就能表示数据的两种状态。

如果我们将int作为位图的数据元素类型,那么一个元素就有32个比特位,第一个int值就可以表示映射值为0~31的数据的两种状态,第二个int值就表示32~65......这样,每一个int值都可以存储32个数据,极大程度减少了空间消耗

那么怎么求出数据在位图中的映射值呢?

以位图数据元素为int为例,我们拿到一个数x,进行如下计算:

        i = x / 32;

        j = x % 32;

这样,i 就表示x在第 i 个int值当中;j 表示它在该值的第 j 位。

二、位图的实现

        接下来我们尝试用c++代码实现一个简易的位图。

位图的三大关键接口

  • set -- 将一个数在位图中的对应位设置为1(相当于插入数据)
  • unset -- 将一个数在位图中的对应位设置为0(相当于删除数据)
  • test -- 判断一个数在位图对应位的值是1还是0,是1返回true,是0返回false。(查找)

1. 结构定义

        首先是位图结构定义:

#include <vector>template<size_t N>
class BitSet
{
public://插入void set(size_t x);//删除void unset(size_t x);//查找bool test(size_t x);
private:std::vector<int> _bs;//用vector表示位图
};

这里我们使用vector容器来表示位图,其中将int作为元素,每个元素表示32个数据的状态。

2. 构造函数

        在构造函数当中,我们需要给位图开辟相应的内存。代码如下:

//构造函数
BitSet()
{//在模板中传入比特位的个数,这里求出需要开辟的int型元素个数_bs.resize(N / 32 + 1);
}

3. 三大接口实现

set

        要将一个数x在位图中对应的位标记为1,且不影响其他位的值,我们的做法是:先求出映射的位置(之前已经提到),然后通过位运算,将1左移 j 位,然后与数组的第 i 个值进行“按位或”运算。

注:无需考虑大小端等底层存储问题,因为左移就是会从低位向高位移动。

 代码实现:

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

unset

        unset将位图的对应位标记为0。实现思路:求出映射位置,将1左移 j 位,然后按位取反,再与数组的第 i 个值进行“按位与”运算。代码实现:

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

test

        test用于判断一个数在位图对应位的值是1还是0。思路:也是先求出映射位置,然后将1左移j位,然后与数组的第i个值进行“按位与”运算,判断运算结果是否非0。非0说明该位一定是1,否则该位是0。代码实现:

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

总代码

        位图总实现代码如下:

#include <vector>template<size_t N>
class BitSet
{
public://构造函数BitSet(){//在模板中传入比特位的个数,这里求出需要开辟的int型元素个数_bs.resize(N / 32 + 1);}//插入void set(size_t x){size_t i = x / 32;size_t j = x % 32;_bs[i] |= (1 << j);}//删除void unset(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;//用vector表示位图
};

4. 测试

        接下来我们给一串数据,测试位图的功能:

#include "BitSet.h"
#include <iostream>
using namespace std;int main()
{BitSet<100> bs;//创建一个一百位的位图int a[] = { 6,22,5,81,7,2,15,94,82,0,3,56,1,17,9,66,49 };//遍历数据,存入位图for (auto& e : a){bs.set(e);}int n = 0;while (cin >> n){if (bs.test(n)) cout << "存在" << endl;else cout << "不存在" << endl;}return 0;
}

运行结果:

三、 标准库的位图

        c++标准库中也实现了位图,我们可以直接使用。注意:使用时需要包含头文件<bitset> 。

标准库位图查阅:bitset - C++ Reference

可以看到,相比我们实现的位图,标准库的位图还实现了operator[]sizeto_string等接口,使用起来更加灵活。这里的接口使用大家可以自行了解,这里博主就不过多赘述。

需要注意:对于标准库的位图,它的数据元素是存放在对象内部的,并不像我们一样在堆区开辟空间。所以无法使用它直接创建超大位数的位图(如UINT_MAX位),此时可以考虑直接将位图对象从堆中动态申请:

std::bitset<UINT_MAX>* p = new std::bitset<UINT_MAX>();//在堆区中创建对象

四、位图的优缺点

优点:由于位图本质是哈希表,所以其增删查改速度极快,时间复杂度为O(1),且相比传统哈希表更加节省空间,适用于海量数据处理的场景。

缺点:只能对整形数据做出处理,无法表示浮点数或其他类型数据的状态。

小技巧:如果需要用表示数据的多种状态(如重复出现的个数等),可以考虑将多个位图配合起来使用。

总结

         位图,这一简洁而高效的数据结构,以其独特的二进制存储方式,在数据处理领域展现了强大的生命力。尽管存在局限性,它仍是解决特定问题的利器,并为更高级的数据结构,如布隆过滤器奠定了基础。在数据爆炸的时代,位图的思想将继续闪耀,为高效数据处理提供源源不断的灵感。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

相关文章:

【数据结构进阶】位图

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;数据结构 目录 前言 一、位图的概念与结构 二、位图的实现 1. 结构定义 2. 构造函数 3. 三大接口实现 set unset test 总代码 4. 测试 三、 标准库的…...

[极客大挑战 2019]BabySQL—3.20BUUCTF练习day4(3)

[极客大挑战 2019]BabySQL-3.20BUUCTF练习day4(3) 做题过程 打开是以下页面&#xff08;前几天有它的第一版和第二版出现&#xff09;输入1’ 回显以下内容&#xff08;还是字符型以单引号闭合&#xff0c;因为有报错信息回显&#xff09; 输入1 order by 4%23回显成这个 被过…...

`sscanf` 和 `scanf` 的区别

sscanf 和 scanf 都是 C 语言中用于从字符串中读取格式化输入的函数&#xff0c;但它们的主要区别在于输入源的不同。 1、### scanf scanf 函数用于从标准输入&#xff08;通常是键盘&#xff09;读取格式化的输入。它的原型如下&#xff1a; int scanf(const char *format, .…...

JVM 学习前置知识

JVM 学习前置知识 Java 开发环境层次结构解析 下图展示了 Java 开发环境的层级关系及其核心组件&#xff0c;从底层操作系统到上层开发工具&#xff0c;逐步构建完整的开发与运行环境&#xff1a; 1. 操作系统&#xff08;Windows, MacOS, Linux, Solaris&#xff09; 作用&…...

数智读书笔记系列021《大数据医疗》:探索医疗行业的智能变革

一、书籍介绍 《大数据医疗》由徐曼、沈江、余海燕合著&#xff0c;由机械工业出版社出版 。徐曼是南开大学商学院副教授&#xff0c;在大数据驱动的智能决策研究领域颇有建树&#xff0c;尤其在大数据驱动的医疗与健康决策方面有着深入研究&#xff0c;曾获天津优秀博士论文、…...

Oracle 常用语法汇总

系列文章目录 本文对Oracle 常用的语法进行汇总 文章目录 系列文章目录一、Oracle 表&表字段操作&#xff1a;1.1 DDL语句(数据定义语言)Create、Alter、Drop、Truncate&#xff1a;1.1.1 建表&#xff1a;建表&#xff1a;注释COMMENT :表中字段的约束&#xff1a;表中字…...

解决python配置文件类configparser.ConfigParser,插入、读取数据,自动转为小写的问题

配置类 [Section1] Key_AAA Value[Section2] AnotherKey Value默认情况下&#xff0c;ConfigParser会将ini配置文件中的KEY&#xff0c;转为小写。 重载后配置类&#xff1a; 继承类从configparser.ConfigParser改为configparser.RawConfigParser重载方法optionxform&#…...

第一天 UnityShader的结构

Shader初学者的学习笔记 第一天 Unity Shader的结构 文章目录 Shader初学者的学习笔记前言一、Unity Shader结构二、Unity Shader结构解析① Properties② Tags③ RenderSetup(可选状态)④ Name⑤ [Tags]⑥ [RenderSetup]⑦ 顶点着色器和片元着色器的代码 (Unity最聪明的孩子)…...

什么是 BA ?BA怎么样?BA和BI是什么关系?

前几天有朋友在评论区提到了BA这个角色&#xff0c;具体是干什么的&#xff0c;我大概来说一下。 什么是BA BA 英文的全称是Business Analyst&#xff0c;从字面上意思就是商业分析师&#xff0c;做过商业智能BI项目的应该比较了解。实际上以我个人的经验&#xff0c;BA 的角…...

Jmeter旧版本如何下载

1.Jmeter最新版本下载位置 https://jmeter.apache.org/download_jmeter.cgi2.Jmeter旧版本下载位置 https://archive.apache.org/dist/jmeter/binaries稳定版本&#xff1a;5.4.1...

Python帕累托图(Pareto Chart): 从数据排序到决策优化

帕累托图&#xff08;Pareto Chart&#xff09;是一种基于80/20法则的经典数据可视化工具&#xff0c;广泛应用于质量管理、项目管理、业务分析等领域。本文将从其原理、构成、实现方法到应用场景进行全面解析&#xff0c;并附Python代码示例。 一、帕累托图的定义与起源 帕累…...

Linux中执行 ifconfig 命令时提示 “未找到命令”

在 Linux 系统里&#xff0c;若执行 ifconfig 命令时提示 “未找到命令” 通常是由于系统没有安装 net-tools 包&#xff0c;或者该命令不在系统的 PATH 环境变量所包含的路径中 安装 net-tools 包 # Ubuntu/Debian sudo apt update sudo apt install net-tools# CentOS 7 及以…...

Python---数据分析(Pandas六:二维数组DataFrame,DataFrame的创建,DataFrame的属性)

一、 二维数组DataFrame DataFrame 是 Pandas 中的一个表格型的数据结构&#xff0c;包含有多列的数据&#xff0c;每列可以是不同的值类型(数值、字符串、布尔型等)&#xff0c;DataFrame 即有行索引也有列索引&#xff0c;可以被看做是由 Series 组成的字典。 二、DataFrame的…...

内网安全-横向移动Kerberos 攻击SPN 扫描WinRMWinRSRDP

1.WinRM&WinRS 条件&#xff1a; 双方开启winrm winrs服务 2008版本以上默认开启&#xff0c;win 7默认关闭 检测使用cs内置端口扫描5985开放情况 进行连接 winrs -r:http://192.168.93.30:5985 -u:administrator -p:Whoami2021 whoami 2.内网-spn shell setspn -T …...

深入理解 lt; 和 gt;:HTML 实体转义的核心指南!!!

&#x1f6e1;️ 深入理解 < 和 >&#xff1a;HTML 实体转义的核心指南 &#x1f6e1;️ 在编程和文档编写中&#xff0c;< 和 > 符号无处不在&#xff0c;但它们也是引发语法错误、安全漏洞和渲染混乱的头号元凶&#xff01;&#x1f525; 本文将聚焦 <&#…...

使用uniapp的vite版本进行微信小程序开发,在项目中使用mqtt连接、订阅、发布信息

1、保证在微信公众平台配置socket合法域名 2、项目中使用mqtt 建议在package.json中配置"mqtt": “4.1.0”&#xff0c;使用这个版本的依赖 页面中引入mqtt并配置连接 // ts-ignoreimport * as mqtt from mqtt/dist/mqtt.js; //要使用这里面的const state reacti…...

Trae 实战深度揭秘,开启高效编程新时代

导语 在AI编程工具层出不穷的当下,Trae凭借其独特的功能和强大的性能脱颖而出。它不仅是一款工具,更是提升编程效率、突破开发瓶颈的得力助手。本文将带你深入Trae实战,从项目创建到复杂代码优化,全方位展示Trae的魅力,让你迅速掌握这一编程利器。 一、Trae的安装与环境…...

SEARCH-R1:大型语言模型的多轮搜索推理革命

当AI学会"边搜索边思考" 2025年&#xff0c;语言模型领域迎来重大突破——SEARCH-R1框架通过强化学习&#xff08;RL&#xff09;让大模型实现"动态搜索自主推理"的协同进化。这项技术不仅让模型在回答"泰坦尼克号沉没时的船长是谁"时能自动检索…...

红数码影视(RED Digital Cinema)存储卡格式化后的恢复方法

红数码影视(RED Digital Cinema)的摄像机可以生成两种RAW级高清视频文件&#xff0c;一种是R3D&#xff0c;一种是MOV。其中MOV属于苹果(apple)公司的QT视频封装结构&#xff0c;使用的视频编码是Apple ProRes;而R3D则是RED公司自创的RAW视频文件&#xff0c;这种文件解码需要使…...

关于TVS管漏电流的问题?

问题描述&#xff1a; 在量产的带电池故事机生产中&#xff0c;工厂产线测试电流时&#xff0c;有1台机器电流比正常机器大10mA左右。 原因分析&#xff1a; 1、分析电路原理图&#xff0c;去除可能出现问题的电压或器件&#xff08;不影响系统&#xff09;&#xff0c;发现…...

LS-NET-004-简单二层环路解决(华为锐捷思科)

LS-NET-004-简单二层环路解决&#xff08;华为锐捷思科&#xff09; 以下是为您准备的二层环路示意图及解决方案&#xff0c;包含四大厂商配置对比&#xff1a; 一、Mermaid 二层环路示意图 graph TD SW1 -->|Gi0/1| SW2 SW2 -->|Gi0/2| SW3 SW3 -->|Gi0/3| SW1 SW1…...

区块链交易所平台开发全解析

在数字化飞速发展的今天&#xff0c;区块链技术已成为金融领域的核心驱动力之一。作为数字货币交易的关键平台&#xff0c;区块链交易所的开发不仅涉及复杂的技术环节&#xff0c;还需要兼顾用户体验、安全性、合规性等多个方面。本文将深入探讨区块链交易所平台的开发流程、关…...

Redis 面试思路

分布式redis面试思路俩点 高性能 高并发 高性能 1.存储在内存 所以速度快 2. 线程模型 io多路复用 监控多个客户端socket 放入队列里面 只是文件分发机制是单线程的 处理队列中的数据 根据不同类型 分发给不同处理器 后面处理的过程 也是多线程的 3. 内存回收机制 定期懒惰 …...

蓝桥杯_拔河_java

佬们能不能对思路二提供点建议&#xff0c;一直过不了T_T。 题目 思路 首先感觉有个坑点&#xff0c;就是可以不用把所有学生都选上&#xff0c;但是一定要保证两个部分学生的编号是连续的。比如一共5个人&#xff0c;编号是{1&#xff0c;2&#xff0c;3&#xff0c;4&#xf…...

fastapi 实践(三)Swagger Docs

fastapi 实践&#xff08;一&#xff09;基础 fastapi 实践&#xff08;二&#xff09;异常捕获 fastapi 实践&#xff08;三&#xff09;Swagger Docs fastapi Swagger 1. FastAPI 交互式 API 文档2. 故障解决2.1. FastAPI 访问 docs 显示空白/加载失败2.2. Swagger 报错&…...

每日一题力扣3248.矩阵中的蛇c++

3248. 矩阵中的蛇 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int finalPositionOfSnake(int n, vector<string>& commands) {int i 0;int j 0;for (int k0;k<commands.size();k) {if (commands[k] "RIGHT")j;else if (comma…...

ReentranLock手写

ReentranLock手写 整体概述 MiniLock 是一个自定义的锁实现&#xff0c;模拟了 Java ReentrantLock 的公平锁机制。公平锁的核心思想是“先来后到”&#xff0c;即线程按照请求锁的顺序依次获取锁&#xff0c;避免线程饥饿。代码使用了以下关键组件&#xff1a; state: 表示…...

Channel-wise Knowledge Distillation for Dense Prediction论文阅读和

paper&#xff1a;https://arxiv.org/pdf/2011.13256.pdf code&#xff1a;https://github.com/open-mmlab/mmrazor 这篇paper主要是商汤开源的mmrazor中提及在detection有效果&#xff0c;我之前记录的几篇sota文章虽然在各自的paper中在detection领域都有提及有增益&#…...

deepSpeed多机多卡训练服务器之间,和服务器内两个GPU是怎么通信

DeepSpeed 在多机多卡训练时,主要依赖 NCCL 和 PyTorch Distributed 进行通信。具体来说,分为服务器之间和服务器内两种情况: 1. 服务器之间的通信(跨节点通信) DeepSpeed 采用 NCCL(NVIDIA Collective Communications Library)作为主要的通信后端,结合 PyTorch Distr…...

Mysql-经典实战案例(10):如何用PT-Archiver完成大表的自动归档

真实痛点&#xff1a;电商订单表存储优化场景 现状分析 某电商平台订单表&#xff08;order_info&#xff09;每月新增500万条记录 主库&#xff1a;高频读写&#xff0c;SSD存储&#xff08;空间告急&#xff09;历史库&#xff1a;HDD存储&#xff0c;只读查询 优化目标 …...