位图即bitmap,位图法就是用一个位存放某个数据的存在状态,比较适合处理大数据,通常用于判断一个数字是否存在。先举个简单的例子,对于斐波那契数列{1,2,3,5,8,13}来说,假如用20位来记录它们的存在状态,那么序列为
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0
我们只需要用一个bool数组来记录它们的状态就可以了,在查询时候可以通过O(1)的时间复杂度完成查询。在C/C++语言中,最小的数据类型也需要一个字节,比如bool类型就是一个字节,占用8位。但如果用一个字节来记录一个数字的状态,也太浪费空间了,如果做一些优化,用一个位记录一个数的状态,空间将变为原来的八分之一,大大节省了存储空间。
![bb55db4cb2cda090062f4676b7363b8b.png](https://i-blog.csdnimg.cn/blog_migrate/3e6c6eb3471e62465162083e67fdc0f6.jpeg)
假如我们定义了一个int类型的数组,用这个数组的每一个记录数据状态,那么需要有两个操作:设置状态和读取状态。
设置状态
在一个数值x的设置状态时,需要先找到x在哪里,包括int数值下标和下标对应位的位置。对应int类型来说,占用4字节,一共32位。先来找int数值下标,通过x / 32就可以找到int数值的下标;再来找下标对应位的位置,由于x在位图中只在其中一个位置值为1,那么通过x&31就找到了下标对应位的位置,然后再把这位设置为1。代码如下
void set(int x) { bitmap[x >> 5] |= 1 << (x & 32);}
读取状态
根据设置状态的分析,读取状态思路差不多,通过&符号取出结果就可以了,代码如下
bool get(int x) { return bitmap[x >> 5] & (1 << (x & 32));}
那么,在做素数筛选时候可以不用bool数组了,直接用位图,代码如下
![a80dd834da9d95174675f0e9ffad4c9d.png](https://i-blog.csdnimg.cn/blog_migrate/93258e5a1ea98d06625a3d6a6de57b36.jpeg)
这样的写法大大节省了使用的内存空间,特别是在数据量比较大的时候优势体现得比较明显,例如下面的题目。
给出40亿个不重复的unsigned int的整数,没排过序,再给一个指定的数,判断这个数是否在那40亿个数中存在?
首先,unsigned int的最大值为2^32-1=4294967295,大约为42亿多,如果用bool数组来存状态,那么就需要开这么大的数组,先不说普通机器能不能申请到这么多内存,就算能内存也耗费太大了。如果通过bitmap位图来做,空间减少到原来的八分之一,申请(4294967296 / 32) * 4 / (1024 * 1024) = 512MB的内存就够用了。
在C++的STL中,有一个bitset位图容器,它定义的数据就是占一个bit,直接使用它实现位图更方便,用法如下
bitset bitmap;bitmap.set();bitmap[3] = 1;bitmap[5] = 1;
需要包含头文件#include 。