位图
1、概念
位图法就是bitmap的缩写,所谓bitmap,就是用每一位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。
我们知道一个1G=1024M,1M=1024K,1K=1024byte,1byte=8bit,所以1个字节等于8bit,也就是8个二进制位,位图法的概念是用一个位(bit)来标记某个数的存放状态,所以节省了大量的空间。
C++的STL中有bitmap类,它提供了很多方法,详见:http://www.cplusplus.com/reference/stl/bitset/
2、数据结构
unsigned int bit[N];
在这个数组里面,可以存储 N * sizeof(int) * 8个数据,但是最大的数只能是N * sizeof(int) * 8 - 1。假如,我们要存储的数据范围为0-15,则我们只需要使得N=1,这样就可以把数据存进去。如下图:
如果要记录数据【5,7,10】,如下图,在对应位置上记录即可。
3、基础知识
a. 数据的比特位
所有比特的编号方法是:从低字节的低位比特位开始,第一个bit为0,最后一个bit为 n-1。
比如,给出一个数组:int[] array = new int[4]。那么:
a[0] -- a[4] 的比特位分别为:0--31,32--63,64--95,96--127
下面我们依据一个程序探究数组比特位的编号:
#include <iostream> using namespace std; int main() { int array[4] = {0,0,0,0}; for (int i = 0; i < 4; i++) { array[i] = 16; } for (int i = 0; i < 4; i++) { array[i] = array[i] >> 4; cout << array[i]<<endl; } return 0; }
结果是输出了4个1,也就是说刚开始比特位编排为:0000 0000 0001 0000,使用位运算,使其右移了4位,变为:0000 0000 0000 0001.
b. 位运算和取模
位运算跟取模运算之间联系微妙,具体可从下面的例子中看出来:
100%32;100&31
上述公式的结果是一样的,让我们探究一下他们的原理:
100%32 的取余运算,将取到一百减去3个32之后的余数为4。 100&31是进行按位与运算,31=0001 1111;100=0110 0100,当他们进行按位与时,大于等于32的那部分将给消去,留下的便是余数。
当然上述运算成立的条件便是 32对应位置的数必须是2的N次幂。
4、相关操作
- 读操作
- 写操作
- 清除
#define SHIFT 5 #define MAXLINE 32 #define MASK 0x1F bool getbit(int *bitmap1, int i){ return bitmap1[i >> SHIFT] & (1 << (i & MASK)); } void setbit(int *bitmap, int i){ bitmap[i >> SHIFT] |= (1 << (i & MASK)); } void clearbit(int *bitmap, int i){ bitmap[i >> SHIFT] & ~(1 << (i & MASK)); }
5、应用
a. 枚举
字符串全组合枚举(对于长度为n的字符串,组合方式有2^n种),如:abcdef,可以构造一个从字符串到二进制的映射关系,通过枚举二进制来进行全排序。
null——> 000000
f——> 000001
e——> 000010
ef——> 000011
……
abcedf——> 111111
b. 搜索
设计搜索剪枝时,需要保存已经搜索过的历史信息,有些情况下,可以使用位图减小历史信息数据所占空间。
c. 压缩
(1)在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数?
(2)腾讯面试题:给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?