前言
给你一台 4G 内存的机器,一组 20 亿个无序正整数,如何快速地判断一个正整数 N 是否在这组数字中?
这貌似是一道面试题,大致算一下20亿个整数需要占用的内存:
java中int类型占4个字节,20亿 * 4 / (1024 * 1024 * 1024) ≈ 7.45G
BitMap的概念
简单的说,就是用一个bit位来表示一个特定的意义。比如我们有一个长度为8的bit数组,数组的第一个元素表示0是否存在,数组的第二个元素表示1是否存在。。。那么:
00000010代表1存在;
01000010代表1和6存在;
。。。
但是一个长度为8的bit数组只能表示7是否存在。如果我们想表示10是否存在呢?那么就需要一个长度为11的bit数组。
数据结构
从上面我们可以得到,bit数组的长度取决于最大的那个数值10。
如果我们用byte数组来存储,那么需要byte数组的长度为 10/8 + 1;
下面的代码表示一个简单的BitMap的数据结构:
class BitMap {
private byte[] bits;
private int maxValue;
private int capacity;
public BitMap(int maxValue) {
this.maxValue = maxValue;
capacity = (maxValue >> 3) + 1;
bits = new byte[capacity];
}
}
添加数据
添加数据,需要快速地定位到这个元素要存到byte数组中的哪个位置,这里的位置有两个概念:
- index : 数据保存在byte数组的哪个索引下;
- position : 数据在对应的byte中的位置;
以10为例:
index = 10 / 8;
position = 10 % 8;
定位到10需要保存的位置后,怎么把对应位置的bit改为1呢?这里可以用“位或”运算。将10添加到BitMap中的完整步骤如下:
- 计算index = 10 / 8 = 1;
- 计算position = 10 % 8 = 2;
- 将byte[1] 中的数据与 00000100 做“位或”运算(00000100是对1左移2得到的),目的是将对应的bit改为1
完整的代码如下:
public void add(int num) {
int index = num / 8;
int position = num % 8;
bits[index] |= (1 << position);
}
判断数字是否存在
直接使用“位与”运算即可,代码如下:
public boolean contains(int num) {
int index = num / 8;
int position = num % 8;
return (bits[index] & (1 << position)) != 0;
}
main 方法如下
public class Demo {
public static void main(String[] args) {
BitMap bm = new BitMap(100);
bm.add(1);
bm.add(13);
bm.add(57);
bm.add(100);
System.out.println("3:" + (bm.contains(3) ? "存在" : "不存在"));
System.out.println("13:" + (bm.contains(13) ? "存在" : "不存在"));
}
}
运行结果:
3:不存在
13:存在