现在有这么一个问题:现存在 20 亿个数字,需要知道哪些数字没有出现在里面.这个问题你可能觉得一个 for 循环就能解决。但是有两个问题,第一个就是for循环效率的问题,第二个问题就是 20 亿个数字需要占据的内存空间,例如要存下 20 亿个数字需要多大空间呢?假设使用 int 存储,在 Java 中,int 占 4 字节,1 字节 = 8 位(1 byte = 8 bit,存放 20 亿个数字则需要:(2000000000*4/1024/1024/1024) ≈ 7.45G左右。
面对上面的问题我们肯定不能使用这种方式存储,而使用我们的 Bit-map 就能很好的解决上面的问题。

Bit-map(位图)

简介

Bit-map 的基本思想就是用一个 bit 位来标记某个元素对应的 Value,而 Key 即是该元素。由于采用了 bit 为单位来存储数据,因此在存储空间方面,可以大大节省。(重点:节省存储空间

Bit-map 是一种数据结构,可用于记录大量的 0-1 状态,在很多地方都会用到,比如 Linux 内核(如 inode,磁盘块)、Bloom Filter 算法等,其优势是可以在一个非常高的空间利用率下保存大量 0-1 状态。

我们在上面也说过了,假如要对于 20 亿个 int 数据进行处理呢?

  • 如果每个数字用 int 存储,那就是 20 亿个 int,占用的空间约为 (2000000000*4/1024/1024/1024)≈7.45G
  • 如果能够采用 bit 储,20 亿个数就是 20 亿位,占用的空间约为 (2000000000/8/1024/1024/1024)≈0.233G,那么在存储空间方面可以大大节省。

从上面的对比中立马高下立判,已经无需多言。

那么,问题来了,如何表示一个数呢?

上面也说了,Bit-map 用于记录大量的 0-1 状态,每一位表示一个数,0 表示不存在,1 表示存在,这正符合二进制。

下面我们来看看具体存储:

存储方式

对于0,2,4,6 这四个数,如果存在的话,则可以这样表示:

计算机内存分配的最小单位是字节,也就是 8 位,那如果 8,10,14 也存在,那怎么存呢?8,10,14 我们可以在另一个 8 位上表示:

以此类推。

假设需要排序或者查找的总数N=10000000,1 个 int 占 32 位,那么我们需要申请内存空间的大小为 int b[1 + N/32],其中 N 表示要存储的这些数中的最大值,b[0] 在内存中占 32 为可以对应十进制数 0-31,依次类推:
Bit-map 表为:
b[0]--------->0-31
b[1]--------->32-63
b[2]--------->64-95
b[3]--------->96-127

位移转换:

  • 十进制数 0-N 对应在数组 b 中的下标 公式:index = N / 32 即可,index 即为对应的数组下标。例如 N = 76, 则 index = 76 / 32 = 2,因此 76 在 b[2] 中
  • 十进制数 0-N 对应的 bit 位 bit = N % 32 即可,例如 N = 76,,bit = 76 % 32 = 12
  • 利用移位 0-31 使得对应的 32 bit 位为 1

接下来我们看一下 Bit-map 中添加、删除、查找怎么个实现法。

添加

这里我们想要把数字 13 给放进去,该怎么做呢?

我们从上面的位移转换,可以概括一些公式出来:

  • 数字所在的 Byte = 数字 / 8(比如:13 / 8 = 1,所以 13 在 Byte(1))
  • 数字所在 Byte 中的位=数字 % 8(比如:13 % 8 = 5,所以 13 在 Byte(1) 中的第 5 位(从 0 位开始算),也就是1<<5)

所以,假如待插入数为 M,插入后的 Byte 则为:Byte(M / 8)| 1<<(M % 8)。

移除

还是和上面一样,这里我想把 13 给移除掉怎么做呢?

我们理解了上面的计算,移除就变得很简单了。添加的时候我们是 1<<5 之后进行的或运算,那么移除的时候把 1<<5(00100000)进行取反,也就是~(1<<5)(11011111),然后与 Byte 进行 & (与运算)就可以了。


所以,假如待移除数为 M,移除后的 Byte 则为:Byte(M / 8) & (~(1<<(M % 8)));

查找

前面我们也说过了,每一位代表一个数字,1 表示 存在,0 表示不存在。通过把该位置为 1 或者 0 来达到添加和清除。那么判断一个数存不存在就是判断该数所在的位是 0 还是 1。

假如我们想知道 13 存不存在,那么只需判断 b[0] & (1<<3) 如果这个值是 0,则不存在,如果是 1,就表示存在。

Bit-map 的作用

快速排序

我们还是以上面为例,现在我们对0 - 7 内的 3 个元素(1、3、6)进行排序,这里我们需要 8 个 bit,所以我们需要开辟一个 1Byte(8 bit) 的空间,并且将这些空间的所有 bit 位都要置为 0 。


然后遍历这 3 个元素,首先第一个元素是 1,那么就把 1 对应的位置为1,注意,因为是从 0 开始的,所以要把第 2 个位置置为 1:


然后按照上面的方式以此类推,如下图:


然后遍历一遍 bit 区域,将该位是 1 的位的编号输出(1、3、6),这样就达到了排序的目的,时间复杂度O(n)。

Bit-map 的优缺点也很明显:

优点:

  • 运算效率高,不需要进行比较和移位
  • 占用内存少,比如N=10000000;只需占用内存为N/8=1250000Byte=1.25M

缺点:

  • 所有的数据不能重复。即不可对重复的数据进行排序和查找
  • 只有当数据比较密集时才有优势

快速查询

首先我们先对所有的数字进行一次遍历,然后将相应的转态位改为 1。遍历完以后就是查询,由于我们的 Bit-map 采取的是连续存储(int 数组中的一个元素是 4 字节占 32 位),我们实际上是采用了一种分桶的思想。一个数组元素可以存储32个状态位,那将待查询的数字除以32,定位到对应的数组元素(桶),然后再求余(%32),就可以定位到相应的状态位。如果为 1,则代表该数字存在,否则,该数字不存在。

快速去重

这里是一道面试题:如何从 20 亿个整数中找出不重复的整数的个数,前提是内存不足以容纳这 20 亿个整数,那么你会用什么办法?这时候我们就会联想到 Bit-map,使用 Bit-map 就可以很好的解决,下面关键的问题就是怎么设计我们的 Bit-map 来表示这 20 亿个数字的状态。

一个数字的状态只有三种,分别为不存在,只有一个,有重复。因此,我们只需要 2bits 就可以对一个数字的状态进行存储了,假设我们设定一个数字不存在为 00,存在一次 01,存在两次及其以上为 11,那我们大概需要存储空间 2G 左右。所以可以这样进行操作:

1、把这 20 亿个数字放进去(存储),如果对应的状态位为 00,则将其变为 01,表示存在一次

2、如果对应的状态位为 01,则将其变为 11,表示已经有一个了,即出现多次

3、如果为 11,则对应的状态位保持不变,仍表示出现多次

4、最后,统计状态位为 01的 个数,就得到了不重复的数字个数,时间复杂度为 O(n)

这里顺便说一下,Bloom filter 原理与 Bit-map 类似,Bloom filter 是一个数据结构,它可以用来判断某个元素是否在集合内,具有运行快速,内存占用小的特点,想要了解这方面的知识可以看一下我这篇文章:Redis-布隆过滤器(Bloom Filter)详解

总结

本文主要介绍了 Bit-map 算法的基本原理和作用,其本质上是采用了 Bit 位来表示元素状态,从而在特定场景下能够极大的节省存储空间,非常适合对海量数据的查找,去重,删除等问题的处理。

海量数据处理之 Bit-map 详细讲解相关推荐

  1. 十八道海量数据处理面试题与相关知识讲解

    转载自:http://blog.sina.com.cn/s/blog_6002b97001014mu1.html 第一部分.十五道海量数据处理面试题 1. 给定a.b两个文件,各存放50亿个url,每 ...

  2. Java对象,Map,List,Set数组等相互转换大全(详细讲解,附代码,讲解案例)

    Java对象,Map,List,Set数组等相互转换大全(详细讲解,附代码,讲解案例) Java对象 转 JSON字符串 JAVA对象转MAP Map转java对象 List转map List和Map ...

  3. 关于海量数据处理的各种常用数据结构

    随着互联网的兴起,越来越多的内容被放到互联网中,从而导致海量数据处理受到更多人的重视,尤其是在百度.腾讯等这些涉及海量数据的公司.下面我们简单谈一下关于海量数据处理的一些常用数据结构.包括哈希.bit ...

  4. 海量数据处理方法总结

    目录 海量数据处理 算法与数据结构基础 海量数据处理方法归纳 分而治之 / hash 映射 + hash 统计 + 堆 / 快速 / 归并排序 多层桶结构 Bitmap / Bloom filter ...

  5. 海量数据处理相关面试问题

    常见的海量数据处理.操作的题目: 1.给定a.b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a.b文件共同的url? 2.有10个文件,每个文件1G,每个文件的每一 ...

  6. 10道海量数据处理的面试题

    说明:本文分为俩部分,第一部分为10道海量数据处理的面试题,第二部分为10个海量数据处理的方法总结. 出处:http://blog.csdn.net/v_JULY_v. 第一部分.十道海量数据处理面试 ...

  7. 从hadoop框架与MapReduce模式中谈海量数据处理

    前言 几周前,当我最初听到,以致后来初次接触Hadoop与MapReduce这两个东西,我便稍显兴奋,觉得它们很是神秘,而神秘的东西常能勾起我的兴趣,在看过介绍它们的文章或论文之后,觉得Hadoop是 ...

  8. 从Hadoop框架与MapReduce模式中谈海量数据处理(含淘宝技术架构)

    从hadoop框架与MapReduce模式中谈海量数据处理 前言 几周前,当我最初听到,以致后来初次接触Hadoop与MapReduce这两个东西,我便稍显兴奋,觉得它们很是神秘,而神秘的东西常能勾起 ...

  9. [转+整理]十道海量数据处理面试题与十个方法大总结

        海量数据处理:十道面试题与十个海量数据处理方法总结 作者:July.youwang.yanxionglu. 时间:二零一一年三月二十六日 本文之总结:教你如何迅速秒杀掉:99%的海量数据处理面 ...

  10. 海量数据处理相关算法及数据结构【转】

    积淀 何谓海量数据处理? 所谓海量数据处理,无非就是基于海量数据上的存储.处理.操作.何谓海量,就是数据量太大,所以导致要么是无法在较短时间内迅速解决,要么是数据太大,导致无法一次性装入内存. 那解决 ...

最新文章

  1. 在C#中用COM操作CAD
  2. C# 关键字Event
  3. monty python dead parrot-BBC十大英剧神作出炉!
  4. ASP.NET Core 中文文档 第四章 MVC(4.2)控制器操作的路由
  5. 懒到极致之怒撸一键打包发布系统
  6. Oracle 数字与空值的排序问题
  7. NOIP2002复赛 普及组 第1题
  8. 【2】Docker数据卷
  9. linux显卡性能测试工具,Linux系统中A/N显卡通用计算性能测试
  10. java正则表达式tab_Linux下如何使用grep命令查找带有tab(退格)的字符
  11. 专注于分布式存储计算技术及其应用url http://www.nosqlnotes.net/
  12. python获取网站window全局对象或方法的返回值
  13. Sql语句查询当天本周本月记录的where条件
  14. 为什么微信小程序也能做游戏?
  15. oracle技术之oracle备份恢复概述
  16. Java常见概念(POJO\javabean\DTO\PO\BO\VO)浅析
  17. AVR-GCC与AVR单片机C语言开发,[推荐]AVR 单片机与GCC 编程 教程
  18. java读写Txt文件
  19. 电子计算机审计,计算机审计存在的风险
  20. R语言使用aov函数进行单因素方差分析(One-way ANOVA)、使用multcomp包的glht函数检验组均值之间所有成对对比差异、使用plot函数可视化Tukey HSD两两均值比较图

热门文章

  1. 在使用开源IOT平台Thingsboard时,遇到的编译问题一initial exceeded maximum budget
  2. 秒表工具类StopWatch
  3. 微信公众号:支付宝支付
  4. 票房突破9亿,翻拍片《误杀》凭什么收获票房口碑双丰收?
  5. Qmail+Vpopmail 安装相关文档
  6. 老铁们来来来,实战STM32
  7. 30天简单了解Java-Day5深入类和对象
  8. 阿里云服务器CPU超分型专有宿主机创建v5实例
  9. No Such Property: Scope For Class: Com.android.build.gradle.internal.variant.ApplicationVariantData
  10. python夯实基础日记-字典、集合、分支、循环