位图布隆过滤器(附面试题)

news2025/1/20 1:42:28

文章目录

目录

文章目录

前言

一 . 位图

1.1 面试题

1.2 位图概念

1.3 位图的实现

1.4 位图的应用

二 . 布隆过滤器

2.1 布隆过滤器提出

2.2布隆过滤器概念

2.3 布隆过滤器的查找

2.4 实现

2.5 布隆过滤器删除

2.6 布隆过滤器优点

2.7 布隆过滤器缺陷

2.8 布隆过滤器使用场景

三 . 海量数据面试题

1. 哈希切割

2. 位图应用

3.布隆过滤器

总结


前言

大家好,今天给大家带来两种数据结构无位图&布隆过滤器


一 . 位图

学到这里,林林总总也是学了很多的数据结构了,那么位图是一种怎样的数据结构呢?我们由一道面试题来引出

1.1 面试题

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

回顾以往学过的数据结构,最简单想到的或许就是直接遍历 时间复杂度为o(n)

或者先排序o(nlogn)再进行一次二分查找o(logn)

来总结一下 遍历 o(n)  二分查找o(nlogn)+o(logn)

如果面试被问到类似的这种问题,上面两种回答绝对是挂了的,那么我们该用什么来解决这个问题?

在这里引出位图

数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比 特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:

1.2 位图概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,整数,数据无重复的场景。通常是用来判 断某个数据存不存在的。

如上例子,10个整数本应该存放需要40个字节,此时用位图只需要3个字节。

1.3 位图的实现

主要就是三个方法的实现1.插入,2.判断数字是否存在,3.将对应的位置置为零

首先看插入

知道了索引就好办了,其它两个直接照抄即可,给出实现

/*
* 位图
* */
public class MyBitSet {
    private byte[] elem;
    private int usedSize;

    public MyBitSet(){
        elem = new byte[1];
    }

    /**
     * @param n 比特数
     */
    public MyBitSet(int n){
        elem = new byte[n/8+1];
    }

    /**
     * 插入数据
     * @param val 以等价于 将数据的对应
     */
    public void set(int val){
        if(val < 0){
            throw new IndexOutOfBoundsException();
        }
        if(val/8+1 > elem.length){
            elem = Arrays.copyOf(elem, val / 8 + 1);
        }
        int arrayIndex = val/8;
        int bitIndex = val%8;
        elem[arrayIndex] |= (1<<bitIndex);
        usedSize++;
    }

    /**
     * 测试数字是否存在
     * @param val
     * @return
     */
    public boolean get(int val){
        if(val < 0){
            throw new IndexOutOfBoundsException();
        }
        if(val/8+1 > elem.length){
            return false;
        }
        int arrayIndex = val/8;
        int bitIndex = val%8;
        return (elem[arrayIndex] & (1 << bitIndex)) != 0;
    }

    /**
     * 将对应位置置为零
     * @param val
     * @return
     */
    public void reSet(int val){
        if(val < 0){
            throw new IndexOutOfBoundsException();
        }
        if(val/8+1 > elem.length){
            elem = Arrays.copyOf(elem, val / 8 + 1);
        }
        int arrayIndex = val/8;
        int bitIndex = val%8;

        elem[arrayIndex] &= ~(1<<bitIndex);
    }

    /**
     * 当前比特位有多少个1
     * @return
     */
    public int getUsedSize() {
        return this.usedSize;
    }

}

1.4 位图的应用

1. 快速查找某个数据是否在一个集合中(已实现)

2. 排序 + 去重(已实现)

3. 求两个集合的交集、并集等

假设我们有两个位图 A 和 B,分别表示集合 A 和集合 B。要求两个集合的交集,我们可以对位图 A 和位图 B 进行逻辑与操作,得到的结果位图即为两个集合的交集。要求两个集合的并集,我们可以对位图 A 和位图 B 进行逻辑或操作,得到的结果位图即为两个集合的并集。

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

二 . 布隆过滤器

2.1 布隆过滤器提出

布隆过滤器(Bloom Filter)是一种空间效率高、误判率低的概率型数据结构,它可以用于判断一个元素是否在一个集合中。布隆过滤器由一个位数组和多个哈希函数组成。当一个元素被加入集合时,它会被哈希函数映射到位数组中的多个位置,将这些位置的值都设为1。查询一个元素是否在集合中时,将该元素经过哈希函数映射到位数组中的多个位置,如果这些位置的值都为1,则说明该元素可能在集合中,否则一定不在。由于哈希函数的随机性,布隆过滤器可能会出现误判,即一个不在集合中的元素被判定为在集合中。但误判率可以通过调整位数组大小和哈希函数个数来控制。

2.2布隆过滤器概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函 数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。

插入

2.3 布隆过滤器的查找

布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。 所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零, 代表该元素一定不在哈希表中,否则可能在哈希表中。

注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因 为有些哈希函数存在一定的误判。

比如:在布隆过滤器中查找"alibaba"时,假设3个哈希函数计算的哈希值为:1、3、7,刚好和其他元素的比 特位重叠,此时布隆过滤器告诉该元素存在,但实该元素是不存在的。

2.4 实现

/**
 * 简单hash函数
 */
class simpleHash {
    private final int cap;
    private final int seed;
    public simpleHash(int cap,int seed){
        this.cap = cap;
        this.seed = seed;
    }

    public int hash(Object key) {
        int h;
        return (key == null) ? 0 : ((cap-1)*seed)*((h = key.hashCode()) ^ (h >>> 16));
    }
}

/**
 * 布隆过滤器
 */
public class BloomFilter {
    private static final int DEFAULT_SIZE = 1 << 24 ;//方便哈希函数的计算
    private static final int [] seeds = new int []{5,7, 11 , 13 , 31 , 37 , 61};
    private final BitSet bitSet;
    private final simpleHash[] simpleHashes;
    private int size = 0;

    public BloomFilter(){
        // 位图
        bitSet = new BitSet(DEFAULT_SIZE);
        simpleHashes = new simpleHash[seeds.length];
        for(int i = 0; i<seeds.length; i++){
            simpleHashes[i] = new simpleHash(DEFAULT_SIZE,seeds[i]);
        }
    }

    public void set(String str){
        if(str == null) return;
        for (simpleHash simpleHash : simpleHashes) {
            int hash = simpleHash.hash(str);
            bitSet.set(hash);
        }
        size++;
    }

    public boolean contains(String str){
        if(str == null) return false;
        for (simpleHash simpleHash : simpleHashes) {
            int hash = simpleHash.hash(str);
            boolean flag = bitSet.get(hash);
            if(!flag) return false;
        }
        return true;
    }
}

2.5 布隆过滤器删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。

比如:删除上图中"tencent"元素,如果直接将该元素所对应的二进制比特位置0,“baidu”元素也被删除了, 因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。

一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈 希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。

缺陷:

1. 无法确认元素是否真正在布隆过滤器中【会有误判】

2. 存在计数回绕【回绕意思为:溢出】


2.6 布隆过滤器优点

1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关 2. 哈希函数相互之间没有关系,方便硬件并行运算

3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势

5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能

6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

2.7 布隆过滤器缺陷

1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白 名单,存储可能会误判的数据)

2. 不能获取元素本身

3. 一般情况下不能从布隆过滤器中删除元素

4. 如果采用计数方式删除,可能会存在计数回绕问题

2.8 布隆过滤器使用场景

1. google的guava包中有对Bloom Filter的实现

2. 网页爬虫对URL的去重,避免爬去相同的URL地址。

3. 垃圾邮件过滤,从数十亿个垃圾邮件列表中判断某邮箱是否是垃圾邮箱。

4. 解决数据库缓存击穿,黑客攻击服务器时,会构建大量不存在于缓存中的key向服务器发起请求,在数据量足够大的时候,频繁的数据库查询会导致挂机。

5. 秒杀系统,查看用户是否重复购买。

三 . 海量数据面试题

1. 哈希切割

问 : 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?, 如何找到top K的IP?

哈希切割

  • 将超过100G的日志文件切分成多个较小的文件,每个文件的大小适合于内存处理。
  • 对每个切分后的文件进行遍历,统计每个IP地址出现的次数。可以使用哈希表(Hash Table)来存储IP地址和对应的出现次数。
  1. 局部top K统计

    • 对每个切分后的文件进行局部统计,得到每个文件的top K IP地址及其出现次数。可以使用小顶堆(Min Heap)来维护当前的top K IP地址。
  2. 合并top K

    • 将每个切分后的文件的top K统计结果进行合并,得到整体的top K IP地址及其出现次数。
  3. 最终top K

    • 对整体的top K统计结果进行合并,得到最终的top K IP地址及其出现次数。

2. 位图应用

1. 给定100亿个整数,设计算法找到只出现一次的整数?

  • 可以使用两个位图来做,在插入某个元素的时候,如果位图1的该位为1,那么就将位图二中该位置修改为一,最后位图1 ^ 位图2,最后得到的结果统计二进制1的个数即可
  • 也可以使用一个位图来解决问题,在插入是给一个元素两个空间的容量,如果全为1,表示出现的次数大于等于两次

2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

  • 用两个位图存储,再把两个位图&,统计结果中二进制位为1的数量

3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

可以使用三个位图来解决

  • 如果整数在bitmap1中对应的位为0,则将其置为1,表示整数已经出现过一次;
  • 如果整数在bitmap1中对应的位为1,但在bitmap2中对应的位为0,则将其置为1,表示整数已经出现过两次;
  • 如果整数在bitmap1bitmap2中对应的位都为1,则将其置为1,并将对应的bitmap1bitmap2中的位清零,然后将对应的位在bitmap3中置为1,表示整数已经出现过三次或更多次。

3.布隆过滤器

1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和 近似算法

精准算法

  • 哈希切割进行分块
  • 使用位图进行统计

近似算法

  • 将每个文件分成许多块,每个块包含一定数量的查询。这样每个块的大小可以适应内存的限制。
  • 对于第一个文件,遍历每个块,使用布隆过滤器对查询进行哈希,并将结果保存到磁盘上。对于第二个文件,同样遍历每个块,使用相同的布隆过滤器对查询进行哈希,并将结果保存到磁盘上
  • 遍历第二个文件中的每个查询,使用布隆过滤器检查该查询是否在第一个文件中出现过。

2. 如何扩展BloomFilter使得它支持删除元素的操作

引入计数器

  • 在传统的 Bloom Filter 中,每个哈希函数对应一个位数组的位置,表示元素的存在与否。现在我们可以将每个位置的 0/1 改为一个计数器,用来记录元素的插入次数。

存储计数器的位可由实际情况进行调整


总结

ok,大家多多理解,我们下一篇博客见!

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

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

相关文章

解决ZED SDK安装后不可用,出现“核心已转储”的闪退问题

在陈述问题简单回顾下ZED SDK安装的步骤 ZED的运行需要显卡支持&#xff0c;cuda加速&#xff0c;因此需要提前安装好显卡驱动以及对应的cuda和cudnn&#xff0c;基础工作在此不再赘述&#xff0c;以下步骤默认已经完成上述准备工作。 建议新建一个虚拟环境以限定ZED使用的py…

SpringIOC第二课,@Bean用法,DI详解,常见面试题Autowired VS Resource

一、回顾 但是我们之前MVC时候&#xff0c;在页面上&#xff0c;为什只用Controller,不用其他的呢&#xff1f; 用其他的好使吗&#xff1f;(我们可以在这里看到&#xff0c;出现404的字样&#xff09; Service ResponseBody public class TestController {RequestMapping(&quo…

前端对浏览器的理解

浏览器的主要构成 用户界面 &#xff0d; 包括地址栏、后退/前进按钮、书签目录等&#xff0c;也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分。 浏览器引擎 &#xff0d; 用来查询及操作渲染引擎的接口。 渲染引擎 &#xff0d; 用来显示请求的内容&#…

基于ResNet18网络完成图像分类任务

目录 1 数据处理 1.1 数据集介绍 1.2 数据读取 1.3 构造Dataset类 2 模型构建 3 模型训练 4 模型评价 5 模型预测 6 什么是预训练模型和迁移学习 7 比较“使用预训练模型”和“不使用预训练模型”的效果。 总结 在本实践中&#xff0c;我们实践一个更通用的图像分类任务…

陶博士月线反转6.4 python 化代码

陶博士月线反转6.4 python 化代码 量化系统有好几个月没有进度了&#xff0c;之前一直纠结策略问题&#xff0c;无从下手。最近和量化的同学聊了下&#xff0c;还得先把自动交易流程先跑起来后面再慢慢优化策略。 所以先拿陶博士的月线反转6.4 python 化&#xff0c;作为试水的…

最常用的Linux命令#程序员

最常用的Linux命令 #程序员 #linux 1. 文件和目录管理 icon2. 文件查看和编辑 3.流程管理 4. 系统信息 5. 用户和组管理 6. 网络配置与监控 7. 包管理

产品学习之路(一)

在做好开发的同时&#xff0c;还需要熟悉产品业务逻辑&#xff0c;不能为了功能而做功能&#xff0c;要从产品经理的角度去看待每个需求和客户痛点所在&#xff0c;这样针对产品设计出来的东西自己也有发言权&#xff1b; 目前作为一名前端开发人员&#xff0c;也在自学产品知识…

史上最全低代码平台盘点!三分钟盘点2023年顶尖二十个低代码平台!

史上最全低代码平台盘点&#xff01;三分钟盘点2023年顶尖二十个低代码平台&#xff01; 什么是低代码平台&#xff1f;2023年顶尖二十大低代码平台&#xff0c;哪个值得一试&#xff1f;低代码平台应该如何选择&#xff1f;本篇&#xff0c;我们将为大家盘点顶尖的十大低代码平…

tcexam 本地容器化搭建

PS: 参考[使用docker搭建tcexam在线考试平台-CSDN博客]&#xff0c;只记录和总结容器创建和install步骤 1. Git下载 tcexam_docker 工程源码至宿主机的 ~/git/ 目录 mkdir ~/git && cd ~/git git clone https://gitee.com/39627020/tcexam_docker.git mv tcexam_doce…

计算机网络体系的形成

目录 1、开放系统互连参考模型OSI/RM 2、两种国际标准 3、协议与划分层次 4、网络协议的三要素 5、划分层次 &#xff08;1&#xff09;文件发送模块使两个主机交换文件 &#xff08;2&#xff09;通信服务模块 &#xff08;3&#xff09;接入网络模块 6、分层带来的好…

Redis--14--BigKey 和 热点Key

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 BigKey1.什么是bigkey2.bigkey的危害3.发现bigkeyscan 4.解决bigkey 什么是热点Key&#xff1f;该如何解决1. 产生原因和危害原因危害 2.发现热点key预估发现客户端…

leetcode:232. 用栈实现队列

一、题目 原题链接&#xff1a;232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 函数原型&#xff1a; typedef struct //我的队列结构定义 { } MyQueue; MyQueue* myQueueCreate() //我的队列创建及其初始化 void myQueuePush(MyQueue* obj, int x) //我的队…

【Python】tensorflow学习的个人纪录(1)

def learn(self, s, r, s_):s, s_ s[np.newaxis, :], s_[np.newaxis, :]v_ self.sess.run(self.v, {self.s: s_})td_error, _ self.sess.run([self.td_error, self.train_op],{self.s: s, self.v_: v_, self.r: r})return td_error代码步入&#xff1a; v_ self.ses…

华为攻击防范简介

定义 攻击防范是一种重要的网络安全特性。它通过分析上送CPU处理的报文的内容和行为&#xff0c;判断报文是否具有攻击特性&#xff0c;并配置对具有攻击特性的报文执行一定的防范措施。 攻击防范主要分为畸形报文攻击防范、分片报文攻击防范和泛洪攻击防范。 目的 目前&…

qt-C++笔记之QStringList

qt-C笔记之QStringList —— 杭州 2023-12-03 文章目录 qt-C笔记之QStringList1.1.官方文档第一部分翻译&#xff1a;继承自QList\<QString\>-初始化-添加字符串1.2.迭代字符串1.3.join()和split()1.4.filter()1.5.lastIndexOf()1.6.indexOf()1.7.replaceInStrings()以及…

react之封装有无Token(路由权限控制)的高阶组件

TOC 前景 有些路由页面内的内容信息比较敏感&#xff0c;如果用户没有经过登录获取到有效Token&#xff0c;是没有权限跳转的&#xff0c;根据Token的有 无控制当前路由是否可以跳转就是路由的权限控制 技术方案 实现步骤 1.在 components 目录中&#xff0c;创建 AuthRoute/in…

微机原理——定时器8253(8254)学习1

目录 定时类型 8253内部结构框图 8253命令字 六种工作方式及输出波形 计数初值的计算与装入 8253的初始化 定时类型 可编程定时器8253&#xff1a;&#xff08;内部采用的是16位 减法计数器&#xff09; 8253内部结构框图 8253命令字 8253有三个命令字&#xff1a;方式命…

flutter使用动态路由传参的最小案例

flutter中使用动态路由传递参数的封装案例&#xff0c;子组件页面只需要接收arguments参数即可&#xff0c;参数是一个map&#xff0c;里面包含有所需要的参数&#xff0c;类似于json。在MaterialApp中配置onGenerateRoute&#xff0c;然后动态判断传递参数&#xff1a; route…

Python程序员入门指南:就业前景

文章目录 标题Python程序员入门指南&#xff1a;就业前景Python 就业数据Python的就业前景SWOT分析法Python 就业分析 标题 Python程序员入门指南&#xff1a;就业前景 Python是一种流行的编程语言&#xff0c;它具有简洁、易读和灵活的特点。Python可以用于多种领域&#xff…

Mininet学习记录(常用命令+创建网络拓扑+OpenDaylight显示拓扑结构)

目录 1.Mininet简介2.Mininet常用命令2.1创建网络拓扑常用参数2.2常用的内部交换命令3.创建网络拓扑的三种方式3.1通过命令行创建3.2通过miniedit可视化界面创建3.3通过python脚本创建4.问题总结1.Mininet简介 Mininet 是由一些虚拟的终端节点 (end-hosts) 、交换机、路由器连接…