位图介绍(bitmap)
1、位图(Bitmap)简介
位图是一种非常常见的结构,它使用每个二进制位来存放一个值的状态,正因为这个性质,它经常被用在数据压缩或者是索引等方面。
有这样一道题:给40亿个不重复的无符号整数,没有经过排序,然后再给一个树,如何快速判断这个数是否在40亿个数之中?在这里如果我们实打实的存放40亿个数据在内存中,那会占据将近15个G的内存,普通电脑根本无法满足,更不用说在对其进行查找操作了。 那么如果选择位图来存储的话我们只需要差不多500M就够了。大大的节省了内存的消耗。
如下图,我们把1表示存在状态,0表示不存在,那么该组数据中就标记了2和5两个数存在
2、C++位图实现
位图结构需要自己实现,C++并没有现成的数据结构可以用
一个int类型整数占4个字节,一共32位,可以标记32个数的状态,因此,我们把一个整数占用的内存叫做一个单元,一个单元标记32个数据的状态
我们对一个数据进行标记之前需要先确定在那个单元,N/32
在确定在该单元的第几位上 N%32
确定位置之后改变状态
(0-->1) ,采用或运算
(1-->0) ,采用与运算
#include<iostream> #include<string.h> #include<algorithm> #include <vector> #include<bitset> using namespace std; class BitMap { public: //初始化位图的大小,range是要保存的数据个数,1个int数据可以标记32个状态,因为位图是不支持扩容操作的 //所以一开始要确定好位图的大小 //比如要标记1000个数据的状态,需要开int类型的数组大小为1000/32+1 BitMap(size_t range) { _bits.resize((range >> 5) + 1); } //在位图中标记第N个数据 void Set(size_t N) { //先确定在那个单元 size_t index = N >> 5; //在确定在该单元的什么位置 size_t bitNum = N % 32; //或运算对原来为1的状态没有影响,方便我们改变第N个数的状态 _bits[index] |= (1 << bitNum); } //在位图中的第N个数据标记置零 void Reset(size_t N) { size_t index = N >> 5; size_t bitNum = N % 32; //假设bitNum=3,则1<<3 = 1000 // 取反操作 ~(1000) = 0111 _bits[index] &= (~(1 << bitNum)); } //判断第N个数是否被标记 bool check(size_t N) { //假设N=9,9>>5相当于9/(2^5) size_t index = N >> 5; //bitNum=9 size_t bitNum = N % 32; //_bits[0]= 00000000 00000000 00000010 00000000 //_bits[0>>9=00000000 00000000 00000000 00000001 return (_bits[index] >> bitNum) & 1; } private: vector<int> _bits; }; int main() { BitMap bs(1001); bs.Set(5); bs.Set(63); bool ret = bs.check(33); if (ret) { cout << "它在" << endl; } else { cout << "它不在" << endl; } /*bitset<32>t1(~(9 >> 5)); bitset<32>t2(1 << 9); cout << t1<< endl; cout << t2 << endl;*/ system("pause"); return 0; }
go语言实现
package bitmap import "fmt" //位图 type BitMap struct { bits []byte max int } //初始化一个BitMap //一个byte有8位,可代表8个数字,取余后加1为存放最大数所需的容量 func NewBitMap(max int) *BitMap { bits := make([]byte, (max>>3)+1) return &BitMap{bits: bits, max: max} } //添加一个数字到位图 //计算添加数字在数组中的索引index,一个索引可以存放8个数字 //计算存放到索引下的第几个位置,一共0-7个位置 //原索引下的内容与1左移到指定位置后做或运算 func (b *BitMap) Add(num uint) { index := num >> 3 pos := num & 0x07 b.bits[index] |= 1 << pos } //判断一个数字是否在位图 //找到数字所在的位置,然后做与运算 func (b *BitMap) IsExist(num uint) bool { index := num >> 3 pos := num & 0x07 return b.bits[index]&(1<<pos) != 0 } //删除一个数字在位图 //找到数字所在的位置取反,然后与索引下的数字做与运算 func (b *BitMap) Remove(num uint) { index := num >> 3 pos := num & 0x07 b.bits[index] = b.bits[index] & ^(1 << pos) } //位图的最大数字 func (b *BitMap) Max() int { return b.max } func (b *BitMap) String() string { return fmt.Sprint(b.bits) }
等风起的那一天,我已准备好一切