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

Bit-map按位存储--轻松应对面试被问到从10亿个数字中查找指定数是否存在

首先:1G=1024M,1M=1024KB,1KB=1024B(字节) 1G = 102410241024 =1073741824 字节(约等于10亿个字节。)所以10亿个数字,如果是int32位(4个Byte)的话,大概就是4G的内存。

1000000000*4/1024/1024/1024≈3.725 G

显然消耗的内存空间太多了。如果按位存储就不一样了,10亿个数就是10亿位,占用空间约为: 

1000000000/8/1024/1024/1024≈0.116 G

只有原来的 \frac{1}{32}

什么是按位存储(Bit-map)

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

Bit-map每一位表示一个数,0表示不存在,1表示存在。例如我们要存储数字{1,2,3,6},则可以使用Bit-map存储结构如下:

计算机内存分配的最小单位是字节,也就是8位,那如果要表示{12,13,15}怎么办呢?当然是在另一个8位上表示了,如下图所示:

 

有没有感觉这样得结构有点眼熟?不错,好像变成一个二维数组。我们知道1个int占32位,如果我们要存储得数字得最大数字是N,则我们只需要申请一个int数组长度为 int b[1+N/32] 即可存储,其中: 

b[0]:可以表示0~31;
b[1]:可以表示32~63;
b[2]:可以表示64~95;
...

这样,给定任意整数N,那么N/32就得到下标,N%32就知道它在此下标的哪个位置

Bit-map 添加数据

如果现在我们想把5这个数字放进去,怎么做呢?上面提到了,给定任意整数N,那么N/32就得到下标,N%32就知道它在此下标b的哪个位置。首先,5/32=0,也是说它应该在b[0],5%32=5,说明它应该在b[0]的第5个位置,那我们把1向左移动5位,然后与原数据按位或:

 代码如下

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

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

Bit-map 清除数据

假设我们要移除某个数字N,该怎么做呢?只需将该数所在的位置为0即可,那么怎么可以做到呢?为了便于理解,我们还是将结构看成是一个二位数组。则步骤可以归纳如下: N/32得到下标i,N%32得到此下标的位置index; 将1左移index位,就到达index这个数字所代表的位; 按位取反,最后与原数按位与,将改位置置0。 假设我们要6移除,我们用图示来表示以一下执行过程:

 

Bit-map 快速查找数据

前面我们也说了,每一位代表一个数字,1表示有(或者说存在),0表示无(或者说不存在)。通过把该为置为1或者0来达到添加和清除的效果,那么判断一个数存不存在就是判断该数所在的位是0还是1。所以查找相对来说比较简单。假设,我们想知道6在不在,那么只需判断 b[0] & (1<<6) 如果这个值是0,则不存在,如果是1,就表示存在。 

Bit-map 快速排序

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

  1. 首先我们开辟1Byte的空间,将这些空间的所有Bit位都置为0,然后将对应位置为1。
  2. 遍历一遍Bit区域,将该位是1的位的编号输出(2,3,4,5,7),这样就达到了排序的目的,时间复杂度O(n)。

Bit-map 优点:

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

Bit-map 缺点:

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

Bit-map 快速去重

假设让你从10亿个整数中找出不重复的整数的个数,提前是内存不足以容纳这10亿个整数,那么你会用什么办法?使用Bit-map就可以很好的解决。关键的问题就是怎么设计Bit-map来表示这10亿个数字的状态了。

一个数字的状态只有三种,分别为不存在,只有一个,有重复

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

  1. 把这10亿个数字放进去(存储),如果对应的状态位为00,则将其变为01,表示存在一次;
  2. 如果对应的状态位为01,则将其变为11,表示已经有一个了,即出现多次;
  3. 如果为11,则对应的状态位保持不变,仍表示出现多次。
  4. 最后,统计状态位为01的个数,就得到了不重复的数字个数,时间复杂度为O(n)。

升级版问题

1:在这10亿个数字中取第7亿的数字

思考中...

2:对这10亿个数字排序(有重复数字)

思考中...

3:查找一个数N是否存在,若存在这个数在第几位(10亿个数无重复、无序存放)

思考中...


http://www.kler.cn/news/342965.html

相关文章:

  • Oracle 12201非PDBS模式单机部署(静默安装)
  • 洗衣店订单管理:Spring Boot技术突破
  • 使⽤ Override 和 New 关键字进⾏版本控制(C#)
  • python调用父类同名成员
  • 高效批量重命名:Windows系统文件与文件夹管理技巧解析
  • [SQL] 数据库增删改操作
  • 前端在vue项目静态文件夹下引入非默认字体并使用
  • 内衣洗衣机和手洗哪个干净?2024内衣洗衣机实力排行揭晓
  • 毕设---中国移动网站平台管理系统的设计与实现
  • LeetCode518:零钱兑换
  • unity 2d 近战攻击判定的三种方式以及精确获取碰撞点
  • NTO和MPW
  • python 实现even_tree偶数树算法
  • Python入门笔记(三)
  • HTTP长连接和短连接 简介
  • 医学统计学思维导图
  • 前端自定义指令控制权限(后端Spring Security)
  • Solon 3.0 引入 SqlUtils :数据库操作的反朴归真
  • Spring Cloud微服务
  • 访问公司gitlab出现 502 Bad Gateway 错误,已经解决