前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >面试官问:BitMap了解么?

面试官问:BitMap了解么?

作者头像
良月柒
发布于 2020-12-18 08:13:41
发布于 2020-12-18 08:13:41
73400
代码可运行
举报
运行总次数:0
代码可运行

程序员的成长之路

互联网/程序员/技术/资料共享

关注

阅读本文大概需要 7 分钟。

来自:https://www.cnblogs.com/cjsblog/p/11613708.html

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

假设有这样一个需求:在20亿个随机整数中找出某个数m是否存在其中,并假设32位操作系统,4G内存

Java中,int占4字节,1字节=8位(1 byte = 8 bit)

如果每个数字用int存储,那就是20亿个int,因而占用的空间约为 (2000000000*4/1024/1024/1024)≈7.45G

如果按位存储就不一样了,20亿个数就是20亿位,占用空间约为 (2000000000/8/1024/1024/1024)≈0.23G

高下立判,无需多言

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

刚才说了,每一位表示一个数,0表示不存在,1表示存在,这正符合二进制

这样我们可以很容易表示{1,2,4,6}这几个数:

计算机内存分配的最小单位是字节,也就是8位,那如果要表示{12,13,15}怎么办呢?

当然是在另一个8位上表示了:

这样的话,好像变成一个二维数组了

1个int占32位,那么我们只需要申请一个int数组长度为 int tmp[1+N/32] 即可存储,其中N表示要存储的这些数中的最大值,于是乎:

tmp[0]:可以表示0~31

tmp[1]:可以表示32~63

tmp[2]:可以表示64~95

。。。

如此一来,给定任意整数M,那么M/32就得到下标,M%32就知道它在此下标的哪个位置

添加

这里有个问题,我们怎么把一个数放进去呢?例如,想把5这个数字放进去,怎么做呢?

首先,5/32=0,5%32=5,也是说它应该在tmp[0]的第5个位置,那我们把1向左移动5位,然后按位或

换成二进制就是

这就相当于 86 | 32 = 118

86 | (1<<5) = 118

b[0] = b[0] | (1<<5)

也就是说,要想插入一个数,将1左移带代表该数字的那一位,然后与原数进行按位或操作

化简一下,就是 86 + (5/8) | (1<<(5%8))

因此,公式可以概括为:p + (i/8)|(1<<(i%8)) 其中,p表示现在的值,i表示待插入的数

清除

以上是添加,那如果要清除该怎么做呢?

还是上面的例子,假设我们要6移除,该怎么做呢?

从图上看,只需将该数所在的位置为0即可

1左移6位,就到达6这个数字所代表的位,然后按位取反,最后与原数按位与,这样就把该位置为0了

b[0] = b[0] & (~(1<<6))

b[0] = b[0] & (~(1<<(i%8)))

查找

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

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

Bitmap有什么用

大量数据的快速排序、查找、去重

快速排序

假设我们要对0-7内的5个元素(4,7,2,5,3)排序(这里假设这些元素没有重复),我们就可以采用Bit-map的方法来达到排序的目的。

要表示8个数,我们就只需要8个Bit(1Bytes),首先我们开辟1Byte的空间,将这些空间的所有Bit位都置为0,然后将对应位置为1。

最后,遍历一遍Bit区域,将该位是一的位的编号输出(2,3,4,5,7),这样就达到了排序的目的,时间复杂度O(n)。

优点:

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

缺点:

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

20亿个整数中找出不重复的整数的个数,内存不足以容纳这20亿个整数。

首先,根据“内存空间不足以容纳这05亿个整数”我们可以快速的联想到Bit-map。下边关键的问题就是怎么设计我们的Bit-map来表示这20亿个数字的状态了。

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

接下来的任务就是把这20亿个数字放进去(存储),如果对应的状态位为00,则将其变为01,表示存在一次;如果对应的状态位为01,则将其变为11,表示已经有一个了,即出现多次;如果为11,则对应的状态位保持不变,仍表示出现多次。

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

快速查找

这就是我们前面所说的了,int数组中的一个元素是4字节占32位,那么除以32就知道元素的下标,对32求余数(%32)就知道它在哪一位,如果该位是1,则表示存在。

小结&回顾

Bitmap主要用于快速检索关键字状态,通常要求关键字是一个连续的序列(或者关键字是一个连续序列中的大部分), 最基本的情况,使用1bit表示一个关键字的状态(可标示两种状态),但根据需要也可以使用2bit(表示4种状态),3bit(表示8种状态)。

Bitmap的主要应用场合:表示连续(或接近连续,即大部分会出现)的关键字序列的状态(状态数/关键字个数 越小越好)。

32位机器上,对于一个整型数,比如int a=1 在内存中占32bit位,这是为了方便计算机的运算。

但是对于某些应用场景而言,这属于一种巨大的浪费,因为我们可以用对应的32bit位对应存储十进制的0-31个数,而这就是Bit-map的基本思想。Bit-map算法利用这种思想处理大量数据的排序、查询以及去重。

补充1

在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方,右移一位相当于除2,右移n位相当于除以2的n次方。

<< 左移,相当于乘以2的n次方,例如:1<<6 相当于1×64=64,3<<4 相当于3×16=48

>> 右移,相当于除以2的n次方,例如:64>>3 相当于64÷8=8

^ 异或,相当于求余数,例如:48^32 相当于 48%32=16

补充2

不使用第三方变量,交换两个变量的值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 方式一
a = a + b;
b = a - b;
a = a - b;

// 方式二
a = a ^ b;
b = a ^ b;
a = a ^ b; 

BitSet

BitSet实现了一个位向量,它可以根据需要增长。每一位都有一个布尔值。一个BitSet的位可以被非负整数索引(PS:意思就是每一位都可以表示一个非负整数)。

可以查找、设置、清除某一位。通过逻辑运算符可以修改另一个BitSet的内容。默认情况下,所有的位都有一个默认值false。

可以看到,跟我们前面想的差不多

用一个long数组来存储,初始长度64,set值的时候首先右移6位(相当于除以64)计算在数组的什么位置,然后更改状态位

别的看不懂不要紧,看懂这两句就够了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int wordIndex = wordIndex(bitIndex);
words[wordIndex] |= (1L << bitIndex);

Bloom Filters

Bloom filter 是一个数据结构,它可以用来判断某个元素是否在集合内,具有运行快速,内存占用小的特点。

而高效插入和查询的代价就是,Bloom Filter 是一个基于概率的数据结构:它只能告诉我们一个元素绝对不在集合内或可能在集合内。

Bloom filter 的基础数据结构是一个 比特向量(可理解为数组)。

主要应用于大规模数据下不需要精确过滤的场景,如检查垃圾邮件地址,爬虫URL地址去重,解决缓存穿透问题等

如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。

链表、树、散列表(哈希表)等等数据结构都是这种思路,但是随着集合中元素的增加,需要的存储空间越来越大;同时检索速度也越来越慢,检索时间复杂度分别是O(n)、O(log n)、O(1)。

布隆过滤器的原理是,当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组(Bit array)中的 K 个点,把它们置为 1 。

检索时,只要看看这些点是不是都是1就知道元素是否在集合中;如果这些点有任何一个 0,则被检元素一定不在;如果都是1,则被检元素很可能在(之所以说“可能”是误差的存在)。

BloomFilter 流程

1、 首先需要 k 个 hash 函数,每个函数可以把 key 散列成为 1 个整数;

2、初始化时,需要一个长度为 n 比特的数组,每个比特位初始化为 0;

3、某个 key 加入集合时,用 k 个 hash 函数计算出 k 个散列值,并把数组中对应的比特位置为 1;

4、判断某个 key 是否在集合时,用 k 个 hash 函数计算出 k 个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.1-jre</version> 
</dependency>

<END>

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的成长之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
​LeetCode刷题实战469:凸多边形
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,后续每天带大家做一道算法题,题目就从LeetCode上面选 !
程序员小猿
2021/12/17
4560
​LeetCode刷题实战469:凸多边形
计算几何笔记
若向量$(x, y)$旋转角度为$a$,则旋转后的向量为$(xcosa - ysina, y cosa + xsina)$
attack
2018/08/01
1.3K0
计算几何笔记
已知三点求平面法向量
空间已知三点的位置p1(x1,y1,z1),p2(x2,y2,z2),p3(x3,y3,z3),令它们逆时针在空间摆放。这样就可以得到平面的两个向量p1p2(x2-x1,y2-y1,z2-z1),p1p3(x3-x1,y3-y1,z3-z1),而平面法线总是和这两个向量垂直。也就是说,p1p2与p1p3的向量积就是平面的法向量n。
charlee44
2019/08/13
3.5K0
【改革春风吹满地 HDU - 2036 】【计算几何-----利用叉积计算多边形的面积】
我们都知道计算三角形的面积时可以用两个邻边对应向量积(叉积)的绝对值的一半表示,那么同样,对于多边形,我们可以以多边形上的一个点为源点,作过该点并且过多边形其他点中的某一个的多条射线,这样就可以把该多边形变为多个三角形,然后利用叉积求面积即可。 不过要注意,对于三角形可以简单的用叉积的绝对值的一半表示,但对于多边形不可随意将它分割成的几个三角形对应的叉积的绝对值相加,要有一定顺序才可。 对于三角形,有
_DIY
2019/09/11
6610
【改革春风吹满地 HDU - 2036 】【计算几何-----利用叉积计算多边形的面积】
atan和atan2反正切计算
返回值 若不出现错误,则返回 arg 在[−π/2;+π/2][−π/2;+π/2] [- π/2 ; +π/2] 弧度范围中的弧(反)正切( arctan(arg)arctan(arg)arctan(arg) )。值域有限,一四象限,斜率不存在不能求。 2. 使用反三角函数atan2求斜率,原型如下
Enterprise_
2019/03/01
1.6K0
【OpenGL】十三、OpenGL 绘制三角形 ( 绘制单个三角形 | 三角形绘制顺序 | 绘制多个三角形 )
三角形绘制即绘制一个面 , 三个点可以唯一确定一个面 , 四个点及多个点组成的多边形 , 不一定是一个面 ;
韩曙亮
2023/03/28
2.8K0
【OpenGL】十三、OpenGL 绘制三角形  ( 绘制单个三角形 | 三角形绘制顺序 | 绘制多个三角形 )
网页|HTML5 也可以画一画(canvas)
在日常生活中总喜欢涂涂画画写写,这样可以使表达更加直观,记录的也更加详细,而在HTML5中同样可以画一画。canvas意为画布,现实生活中用它来作画,在HTML5中的canvas与之类似,可以称它为“网页中的画布”,有了这个画布便可以轻松的在网页中绘制图形、文字、图片等。
算法与编程之美
2020/04/15
2.5K0
平面中判断点在三角形内算法(同向法)
平面中判断点在三角形内外有很多中算法,文献1中提到了一种同向法,我认为是比较好的解法,兼顾了效率和可理解性。不过这个算法有两个要注意的地方。
charlee44
2021/06/10
1.3K0
【CSS3】CSS3 2D 转换 - rotate 旋转 ② ( 使用 rotate 旋转绘制三角形 )
一、使用 rotate 旋转绘制三角形 ---- 使用 rotate 旋转绘制三角形 的原理 : 先绘制正方形 , 为该正方形设置边框 , 只设置 右侧 和 下方的 边框 , div { width: 40px; height: 40px; border-right: 2px solid black; border-bottom: 2px solid black; } 如果要一
韩曙亮
2023/04/24
4830
【CSS3】CSS3 2D 转换 - rotate 旋转 ② ( 使用 rotate 旋转绘制三角形 )
【CSS3】CSS3 2D 转换 - rotate 旋转 ③ ( 使用 transfrom-origin 设置旋转中心点 | 使用 方位词 / 百分比值 / 像素值 设置旋转中心点 )
为 div 盒子模型 设置 transform: rotate 样式 , 可以使 盒子模型 围绕 中心点 进行 旋转 , 代码如下 :
韩曙亮
2023/10/11
1.1K0
【CSS3】CSS3 2D 转换 - rotate 旋转 ③ ( 使用 transfrom-origin 设置旋转中心点 | 使用 方位词 / 百分比值 / 像素值 设置旋转中心点 )
【OpenGL】十四、OpenGL 绘制三角形 ( 绘制 GL_TRIANGLE_STRIP 三角形 | GL_TRIANGLE_STRIP 三角形绘制分析 )
该模式绘制首先在 glBegin 方法中设置 GL_TRIANGLE_STRIP 参数 , 然后在 glBegin 和 glEnd 之间设置多个点进行三角形绘制 ;
韩曙亮
2023/03/28
1.5K0
【OpenGL】十四、OpenGL 绘制三角形  ( 绘制 GL_TRIANGLE_STRIP 三角形 | GL_TRIANGLE_STRIP 三角形绘制分析 )
随手画个圆,你是怎么画的?我们分析了10万个圆,得到了这样的结论
大数据文摘作品 编译:Niki、吕征达、笪洁琼、Harry 在读本文之前,可以先自己试着从纸上画个圆圈。再回想一下,你是从上面开始画的还是下面呢?顺时针还是逆时针? 在这些问题里,可能隐藏着你来自哪里的线索。 今年十一月,谷歌发布了一款叫“Quick,Draw!”的线上游戏,玩家需要在20秒内画出要求的图案,比如骆驼或洗衣机之类的。(游戏界面传送门:https://quickdraw.withgoogle.com/) 这个游戏的目的远不止让你开心,真正的初衷是运用这些草图让计算机学习人如何绘画。(意味深长啊
大数据文摘
2018/05/24
1.2K0
多边形的点序
凸多边形:Convex polygon,non-self-intersecting polygon, simple polygon说的都是它(定义详见 wiki)。常见的凸多边形有:矩形、三角形等。
用户4363240
2020/01/01
1.7K0
多边形的点序
hover 背后的数学和图形学
前端开发中,hover是最常见的鼠标操作行为之一,用起来也很方便,CSS直接提供:hover伪类,js可以通过mouseover+mouseout事件模拟,甚至一些第三方库/框架直接提供了 hover API ,比如 jQuery 的 hover() 函数。大部分前端开发者在使用这些很方便的方法时,可能并没有思考过 hover 背后的实现原理。
寒月十八
2021/11/19
1.4K0
变换(Transform)(1)-向量、矩阵、坐标系与基本变换
如果要将右侧坐标系变为左侧那种,我们只需要做一些旋转操作,将右侧坐标系顺时针旋转180度,再将整个坐标系水平翻转即可。我们可以通过一定的旋转操作将两个坐标系重合,那么我们就称它们具有相同的旋向性(handedness)。
Zero Two
2024/07/21
4860
(hdu step 7.1.5)Maple trees(凸包的最小半径寻找掩护轮)
求凸包的最小覆盖圆的半径。事实上就是在求完凸包以后再求一下最小覆盖圆即可了。
全栈程序员站长
2022/01/11
3690
(hdu step 7.1.5)Maple trees(凸包的最小半径寻找掩护轮)
【怕啥弄啥系列】Canvas - 基础图形绘制
HTML5 <canvas> 元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成.
神仙朱
2019/08/02
1.1K0
第4章-变换-4.1-基础变换
本节介绍最基本的变换,例如平移、旋转、缩放、剪切、变换级联、刚体变换、法线(normal)变换(不太normal)和逆计算。对于有经验的读者,它可以作为简单变换的参考手册,对于新手,它可以作为对该主题的介绍。这些材料是本章其余部分和本书其他章节的必要背景。我们从最简单的变换开始——平移。
charlee44
2021/12/21
4.1K0
第4章-变换-4.1-基础变换
使用 mesh 实现多边形裁剪图片!Cocos Creator!
mesh 是什么? mesh 是决定一个物体形状的东西。例如在二维中可以是正方形、圆形、三角形等;在三维中可以是正方体、球体、圆柱体等。
张晓衡
2020/02/20
2.2K0
Flash/Flex学习笔记(55):背面剔除与 3D 灯光
Animation in ActionScript3.0 这本书总算快学完了,今天继续:上一回Flash/Flex学习笔记(50):3D线条与填充 里,我们知道任何一个3D多面体上的某一个面,都可以分解为多个三角形的组合。比立方体为例,每个面都由二个三角形组成,但在那一篇的示例中明显有一个问题:不管立方体的某一个面是不是应该被人眼看见(比如转到背面的部分,应该是看不见的),这一面都被绘制出来了。 在这一篇的学习中,我将带大家一起学习如何将背面(即看不见的面)删除掉,即所谓的“背面剔除”。 先做一些预备知识的
菩提树下的杨过
2018/01/22
1.3K0
Flash/Flex学习笔记(55):背面剔除与 3D 灯光
推荐阅读
相关推荐
​LeetCode刷题实战469:凸多边形
更多 >
LV.1
腾讯高级UI工程师
加入讨论
的问答专区 >
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
    本文部分代码块支持一键运行,欢迎体验
    本文部分代码块支持一键运行,欢迎体验