【C++】位图 + 布隆过滤器

目录

  • 1. 位图
    • 1.1. 概念
    • 1.2. 实现
    • 1.3. 应用
  • 2. 布隆过滤器
    • 2.1. 背景
    • 2.2. 概念
    • 2.3. 实现
    • 2.4. 优点
    • 2.5. 缺点
  • 3. 海量数据面试题
    • 3.1. 哈希切割
    • 3.2. 位图应用
    • 3.3. 布隆过滤器
    • 3.4. 总结

1. 位图

1.1. 概念

  1. 位图是一种用于高效地存储和操作集合的数据结构。它的基本思想是使用一个二进制位(0或1)来表示一个元素是否存在于集合中,位图中的每一位对应着集合中的某个元素,可以通过直接访问某个位来判断某个元素在不在集合中。若某个元素在集合中,则对应位为1,否则,对应位为0。

  2. 位图是使用"哈希(映射)"这种思想实现的,计算集合中的元素在位图中的存储位置,采用哈希的直接定址法。适用于海量数据,数据无重复的场景。一般常用于判断某个数据是否存在。

  3. 位图开辟空间时,空间大小为数据类型范围的最大值,不是数据的个数,使用非类型模板参数N。eg : 整数,包括有、无符号整数,最大值为2^32-1,非类型模板参数N=UINT_MAX、0XFFFFFFFF(16进制)、-1(自动转换为size_t),共需要500MB内存空间。

  4. 位图优缺点:高效存储,节省空间;快速查询,时间复杂度为O(1);只适用于整型数据(%);快速集合运算等。

image.png

1.2. 实现

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<vector>using namespace std;namespace zzx{ //位图,采用"哈希"思想来实现的template<size_t N> //非类型模板参数,位图大小开的是数据类型的范围,不是数据的个数class bitset{public:bitset()  //构造函数,提前开辟好空间,防止[]越界访问{_bits.resize(N/32 + 1, 0); //多开辟一个int,eg:66,并将位图的每一位初始化为0}void set(size_t x) //将x映射的位设置为1{int i = x / 32; //x存储在数组的第i个整型数据int j = x % 32;   //x存储在数组的这个整数的第j位_bits[i] |= (1 << j);  //其他位不变,第j位变为1}void reset(size_t x) //将x映射的位设置为0{int i = x / 32;  int j = x % 32;_bits[i] &= ~(1 << j);  //其他位不变,第j位变为0}bool test(size_t x)  //测试x映射位的值是否为1(状态){int i = x / 32;int j = x % 32;return _bits[i] & (1 << j); //整形转bool,0为假,非0为真}private:vector<int> _bits; //底层为数组};
}

位图实现.png

1.3. 应用

  1. 快速查询某一个数据是否在一个集合中。结果是在或不在,只有两种状态,可以用一个二进制比特位来代表数据是否存在的信息。位图中的每一位对应集合中的某个元素,通过直接定址法直接访问某个元素在位图中的存储位,对应位为1表示在,对应位为0表示不在。

  2. 排序 + 去重。集合中的元素在位图中的存储位置,采用哈希的直接定址法,从头到尾打印位图,就可以得到一个有序的集合。位图空间大小为数据类型的范围,大小固定的,重复的元素会映射到同一位,达到去重的目的。

  3. 求两个集合的交集或者并集。将两个集合set到两个位图中,test同时为1的就为交集,否则,为并集。

  4. 操作系统中磁盘块标记。

屏幕截图 2024-04-09 190614.png

2. 布隆过滤器

2.1. 背景

位图只适用于整形数据,对于字符串,位图就无法处理了。

需要先通过哈希函数将字符串转化为整形,在进行映射存储位置,但会出现哈希冲突,从而导致"误判"(不在:对应位为0, 准确的、在:对应位为1, 可能会有"误判”,因为映射位置的冲突,会将不在误判成在)。

为了降低哈希冲突的概率,就有人提出了通过多个不同的哈希函数对key进行不同位置的映射,只要有一个映射位置的值不同,就不在,误判率降低,但仍会有哈希冲突,无法百分百避免。

2.2. 概念

  1. 布隆过滤器是一种数据结构,它实际上是很长的二进制向量和一系列随机映射函数。由一个很长的bit数组和一系列哈希函数组成。

  2. 它的特点是高效的插入、查询,可以用来告诉你"某样东西一定不存在或者可能存在",相比于传统的list、set、map等数据结构,它更高效,占用的空间内存更少,缺点是返回的结果具有概率性,不是确定的。它是用多个哈希函数,将一个数据映射到位图结构中。此方式不仅可以提升查询效率,也可以节省内存空间。

2.3. 实现

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include"bitset.h"struct HashFuncBKDR {//BKDRsize_t operator()(const string& s){size_t hash = 0;for (auto& ch : s){hash *= 131;hash += ch;}return hash;}
};struct HashFuncAP
{// APsize_t operator()(const string& s){size_t hash = 0;for (size_t i = 0; i < s.size(); i++){if ((i & 1) == 0) // 偶数位字符{hash ^= ((hash << 7) ^ (s[i]) ^ (hash >> 3));}else              // 奇数位字符{hash ^= (~((hash << 11) ^ (s[i]) ^ (hash >> 5)));}}return hash;}
};struct HashFuncDJB
{// DJBsize_t operator()(const string& s){size_t hash = 5381;for (auto ch : s){hash = hash * 33 ^ ch;}return hash;}
};template<size_t N, class K = string, class Hash1 = HashFuncBKDR, class Hash2 = HashFuncAP, class Hash3 = HashFuncDJB>
class BloomFilter {   //布隆过滤器
public:void set(const K& key)  //用多个哈希函数,将一个数据映射的位设置为1{int hash1 = Hash1()(key) % M; int hash2 = Hash2()(key) % M; int hash3 = Hash3()(key) % M;_bits.set(hash1);_bits.set(hash2);_bits.set(hash3);}bool test(const K& key)   //检验Key是否存在集合中{ //只要有一个映射位对应的值不同,就不存在,若全部相同,但仍有冲突的可能,导致"误判",无法规避int hash1 = Hash1()(key) % M;  int hash2 = Hash2()(key) % M;int hash3 = Hash3()(key) % M;if (_bits.test(hash1) == false) return false;if (_bits.test(hash2) == false) return false;if (_bits.test(hash3) == false) return false;return true;}private:static const size_t M = 5 * N;  //N为插入的元素个数,M为布隆过滤器的长度zzx::bitset<M> _bits;  
};

屏幕截图 2024-04-09 213655.png

2.4. 优点

  1. 增加、查询元素的时间复杂度位O(K)(K为哈希函数的个数,一般较小),与数据量的大小无关。

  2. 布隆过滤器不需要存储元素本身,对于保密要求比较严格的场合具有较大的优势。

  3. 各个哈希函数之间无联系,方便硬件并行运算。

  4. 数据量很大的时候,布隆过滤器可以表示全集,其他数据结构不能,如:平衡树,需要存储集合中的每个元素,布隆不需要,节省空间。

  5. 对于使用相同哈希函数的布隆过滤器,可以进行交集、并集、差集运算。

2.5. 缺点

  1. 有误判率,存在"假阳性”。因为哈希冲突,会把不在的误判成在。

  2. 不能获取元素本身。

  3. 不能从布隆过滤器中删除元素。

  4. 采用引用计数方式删除,会造成计数回绕、空间浪费等问题。

3. 海量数据面试题

3.1. 哈希切割

哈希切割面试题.png

3.2. 位图应用

位图面试题.png

//只出现一次的整数
template<size_t N>  
class two_bitset1 {
public:void set(size_t x) //插入{//0次(00)->1次(01)->2次及以上(10)if (_bs1.test(x) == 0 && _bs2.test(x) == 0)   _bs2.set(x);else{_bs1.set(x);_bs2.reset(x);}}bool test(size_t x) //找出只出现一次的元素{if (_bs1.test(x) == 0 && _bs2.test(x) == 1)  return true;else  return false;}private:bitset<N> _bs1; //两个位图bitset<N> _bs2;
};two_bitset1<UINT_MAX> tbs;
int a[] = { 1, 3, 8, 4, 1, 6, 3, 7, 4, 10, 2 };
for (auto& e : a)
{tbs.set(e);
}
for (auto& e : a)
{if (tbs.test(e))cout << e << endl;
}
//求交集(需去重)bitset<20> bs1;bitset<20> bs2;int a1[] = { 10, 4, 8, 4, 2, 1, 5, 2 };int a2[] = { 10, 10, 4, 2, 2, 3 }; for (auto& e : a1){bs1.set(e);}for (auto& e : a2){bs2.set(e); }for (int i = 0; i < 20; i++) //去重了{if (bs1.test(i) && bs2.test(i))cout << i << endl;}cout << endl;for (auto& e : a2)  //未去重{if (bs1.test(e) && bs2.test(e))cout << e << endl;}
//出现次数不超过2次的整数
template<size_t N>
class two_bitset2 {
public:void set(size_t x) //插入{//0次(00)->1次(01)->2次及以上(10)if (_bs1.test(x) == 0 && _bs2.test(x) == 0)   _bs2.set(x);else if(_bs1.test(x) == 0 && _bs2.test(x) == 1)  //注意,此处不能为if,因为上面if走完,会继续往下走{_bs1.set(x);_bs2.reset(x);}else{_bs1.set(x);_bs2.set(x);}}bool test(size_t x) //找出只出现一次的元素{if (_bs1.test(x) == 0 && _bs2.test(x) == 0)  return true;else  if (_bs1.test(x) == 0 && _bs2.test(x) == 1)  return true;else if (_bs1.test(x) == 1 && _bs2.test(x) == 0)  return true;return false;}private:bitset<N> _bs1; //两个位图bitset<N> _bs2;
};

3.3. 布隆过滤器

布隆过滤器的面试题.png

3.4. 总结

  1. 海量数据的特征:数据量太大,内存存不下。

  2. 优先考虑具有特点的数据结构能否解决 -》位图、布隆过滤器、堆等。

  3. 其次进行"大事化小",哈希切分,但不能平均切分,切小以后,放到内存中处理。

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

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

相关文章

高并发内存池(四)Page Cache的框架及内存申请实现

目录 一、Page Cache的框架梳理 二、Page Cache的实现 2.1PageCache.h 2.2VirtualAlloc 2.3std::unordered_map _idSpanMap,> 2.4Page Cache.cpp 一、Page Cache的框架梳理 申请内存&#xff1a; 1. 当central cache向page cache申请内存时&#xff0c;page cache先检…

Intel 13/14代不稳定 微星率先发声:密切监视、8月中旬更新微码

不久前&#xff0c;Intel针对14/14代酷睿i9 K系列不稳定的问题发布了最新声明&#xff0c;确认问题源于微代码算法缺陷与电压过高&#xff0c;并承诺将在8月中旬完成新版BIOS的验证&#xff0c;随后发放。现在&#xff0c;微星在各家主板厂商中第一个站出来&#xff0c;表明了态…

Java 使用 POI 导出Excel,实现单元格输入内容提示功能

在使用Apache POI的库生成Excel导入模板的时候&#xff0c;有时候需要对单元格能够输入的内容进行一个提示&#xff0c;该如何实现这个特性呢&#xff1f;下面是一个示例代码&#xff0c;演示如何实现单元格输入内容提示功能。 代码 import org.apache.poi.ss.usermodel.*; im…

Frienda 4 件套幽灵狩猎猫球运动发光猫球 LED 运动激活猫球运动点亮猫狗互动玩具宠物发光迷你跑步健身球

来自 美国亚马逊&#xff1a;商品评论: Frienda 4 件套幽灵狩猎猫球运动发光猫球 LED 运动激活猫球运动点亮猫狗互动玩具宠物发光迷你跑步健身球玩具(亮色) (amazon.com) Kim 1.0 颗星&#xff0c;最多 5 颗星 Battery does not last/ cant replace 2024年5月29日 在美国审核…

lora微调Qwen模型全流程

LoRA 微调 Qwen 模型的技术原理概述 LoRA&#xff08;Low-Rank Adaptation&#xff09;是一种用于大模型高效微调的方法。通过对模型参数进行低秩分解和特定层的微调&#xff0c;LoRA 能在保持模型性能的前提下显著减少训练所需的参数量和计算资源。接下来是对 LoRA 微调 Qwen…

鸿蒙开发—黑马云音乐之首页导航栏

目录 1.底部导航 2.点击导航栏的时候点亮 3.新建tabbar对应的页面并加载 1.底部导航 Entry Component struct Index {State message: string 首页BuildertabBuilder(text:string,img:Resource) {// 未选中状态样式处理Column({ space: 5 }) {Image(img).width(25).border…

[C++进阶]抽象类

一、抽象类 1.抽象类的概念 在虚函数的后面写上 0 &#xff0c;则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类&#xff08;也叫接口类&#xff09;&#xff0c;抽象类不能实例化出对象。派生类继承后也不能实例化出对象&#xff0c;只有重写纯虚函数&#xff0c;派生类才…

unity3d:TabView,UGUI多标签页组件,TreeView树状展开菜单

概述 1.最外层DataForm为空壳编辑数据用。可以有多个DataForm&#xff0c;例如福利DataForm&#xff0c;抽奖DataForm 2.Menu层为左边栏层&#xff0c;每个DataForm可以使用不同样式的MenuForm预制体 3.DataForm中使用ReorderList&#xff0c;可排列配置 4.有定位功能&#xf…

Clickhouse 生产集群部署(Centos 环境)

文章目录 机器环境配置安装 JDK 8安装 zookeeperClickhouse 集群安装rpm 包离线安装修改全局配置zookeeper配置Shard和Replica设置image.png添加macros配置启动 clickhouse启动 10.82.46.135 clickhouse server启动 10.82.46.163 clickhouse server启动 10.82.46.218 clickhous…

《InheriBT行为树》For Unity

InheriBT: Unity Editor中的行为树编辑框架 行为树&#xff08;Behavior Tree&#xff09;是一种广泛应用于人工智能&#xff08;AI&#xff09;领域的决策模型&#xff0c;特别是在游戏开发中。行为树通过分层结构和节点的组合&#xff0c;实现了复杂行为的简洁表达。然而&am…

CPU350% JVM GC频繁并GC不掉EXCEL导出

背景&#xff1a; 有个Excel导出的需求&#xff0c;测试的时候&#xff0c;只要连续导出大量的数据就会导致FAT机器反请求反应迟钝&#xff0c;甚至卡死&#xff0c;无法恢复。 排查&#xff1a; 1 跳板机跳到机器上&#xff0c;查看 项目 ipd 执行ps -ef | grep 项目名称.j…

虚拟机Ubuntu20.04 利用串口调试机械臂

虚拟机Ubuntu20.04 利用串口调试机械臂 串口库问题 由于机械臂使用的是串口进行驱动控制&#xff0c;在python中相关的串口库为serial和pyserial两个&#xff0c;这里我曾踩过雷同时安装了serial与pyserial两个库&#xff0c;导致报错如下所示&#xff1a; AttributeError: m…

数据结构:(1)线性表

一、基本概念 概念&#xff1a;零个或多个数据元素的有限序列 元素之间是有顺序了。如果存在多个元素&#xff0c;第一个元素无前驱&#xff0c;最后一个没有后继&#xff0c;其他的元素只有一个前驱和一个后继。 当线性表元素的个数n&#xff08;n>0&am…

使用Spring Boot与Spire.Doc实现Word文档的多样化操作

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 前言 使用Spring Boot与Spire.Doc实现Word文档的多样化操作具有以下优势&#xff1a; 强大的功能组合&#xff1a;Spring Boot提供了快速构建独立和生产级的Spring应用程序的能力&#xff0c;而Spire.Doc则…

OSError: You are trying to access a gated repo.解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

『 Linux 』用户态与内核态的转换机制及信号检测时机

文章目录 用户态与内核态进程地址空间操作系统的本质 信号的处理时机 用户态与内核态 进程在执行代码的过程中代码必定涉及用户代码,库函数代码及操作系统内核代码; 以简单的printf()函数为例,该函数必定为先执行用户的代码即知道需要调用printf()函数,再执行库(如libc)中的代码…

Java线程同步与通信:wait(), notify(), notifyAll(), sleep()

Java线程同步与通信&#xff1a;wait&#xff08;&#xff09;, notify&#xff08;&#xff09;, notifyAll&#xff08;&#xff09;, sleep&#xff08;&#xff09; 1. wait()2. notify()3. notifyAll()4. sleep()4、总结 &#x1f496;The Begin&#x1f496;点点关注&…

一文带你读懂TCP

文章目录 1 TCP协议1.1 TCP 基础1.1.1 TCP 特性1.2.2 TCP连接数 1.2 TCP 头1.2.1 TCP 头格式1.2.2 MTU&#xff0c;MSS&#xff0c;分片传输 1.3 TCP 连接三路握手1.4 TCP 断开四次挥手1.5 SYN攻击和防范1.6 重传机制1.6.1 超时重传1.6.2 快速重传1.6.3 SACK 1.7 滑动窗口1.8 流…

Linux基础复习(二)

前言 本文介绍了一下Linux命令行基本操作及网络配置 一、 命令行提示含义 [当前用户主机名 工作目录]$ 若当前用户是root&#xff0c;则最后一个字符为# 否则&#xff0c;最后一个字符为$ 二、常用Linux命令及其解释 修改主机名 一般在创建一台主机后会使用hostname相关命…