bitmap学习

来源:互联网 发布:java求3个最小公倍数 编辑:程序博客网 时间:2024/06/06 00:17

主要参考了以下两篇文章
[《编程珠玑》学习总结1—bitmap]
[浅谈bitmap算法]

基本概念

  1. 什么是bitmap
    我自己的理解是:首先是一种map,其次映射到bit位。关键还是它是一种映射,映射到的value由于是bit,比较节省空间。bitmap是一种映射,每一个bit位用来表示key所对应的value是否存在。
  2. 优点和缺点是什么
    优点:节省空间,并且,位运算比较快
    缺点:不能处理重复元素。

思路

  1. bitmap的思路是Hash,所以,需要解决的问题是,如何把一个整数映射到bitmap当中。比如,int a = 0; 它有32位bit,先假设int a = 0; 栈2个字节,那么16位,可以表示16个数字是否存在,从0开始。0000 0000 0000 0000,由于有16位,所以可以用来表示16个数字。比如,
    0000 0000 0000 0001 -> 0 表明0是存在的
    0000 0000 0000 0010 -> 1 表明1是存在的

    1010 0001 0101 0011 -> 表明,0 , 1, 4 ,6 8, 13, 15都是存在的。

  2. 关键就是对于一个val,如何将它映射为一个bitmap。不放这么考虑,对于 int a[2];
    a[0]: 0000 0000 0000 0000 0000 0000 0000 0000 (0 - 31)
    a[1]: 0000 0000 0000 0000 0000 0000 0000 0000 (32 - 63)

这其实相当于二维表,当然在内存中a[0]是byte进行排列的,但是我现在可以忽略这一点。比如,对于32,如何计算它的存储位置?
row = val / 32;
col = val % 32;

对于32而言,row = 1 = 32/32; 所以,存在a[1]里面,col = 0 = 32%32;相当于是如下的存储:
a[0]: 0000 0000 0000 0000 0000 0000 0000 0000 (0 - 31)
a[1]: 0000 0000 0000 0000 0000 0000 0000 000 1 (32 - 63)

所以row_index = val / 32;col_index = val%32, 那么问题来了,a[row_index]这点你是可以做到得,但是由于它已经是变量(最小的操作单位),没有办法对一个变量内部进行寻址。位元素在此处就要派上用途了!对变量内部的col_index进行置位。

具体来说,以32为例,col_index = 0.相当于偏移量为0,需要在偏移量为0的地方进行置位。可以用如下操作 1 << col_index ,将1左移动col_index位,然后再和a[0]进行或操作,那么就完成了对变量内部col_index的置位。

a[row_index] = a[row_index] | ( 1 << col_index ) ,这个就是置位的公式。
a[row_index] = a[row_index] & ~( 1 << col_index ) ,这个是清除操作
return a[row_index] & ( 1 << col_index ), 这个是获取当前位的操作。

代码

#include <cstdio>#define BITPERWORD 32#define SHIFT 5#define MASK 0x1F#define N 32int a[ N/BITPERWORD]; // [0, N)void set( int i ){    a[ i >> SHIFT ] |= ( 1 << ( i & MASK ) ) ;}void clr( int i ){    a[ i >> SHIFT ] &= ~( 1 << ( i & MASK ) ) ;}int test( int i ){    return a[ i >> SHIFT ] & ( 1 << ( i & MASK ) );}int main( void ){    int val = 0;    for( int i = 0; i < N; ++i ){        clr(i);    }    while( scanf( "%d", &val ) != EOF ){        set(val);    }    printf( "-----------------------\n" );    for( int i = 0; i < N; ++i ){        if( test(i) )            printf( "%d\n", i );    }    return 0;}

解释下几个关键操作:
i >> SHIFT = i / 32;
i & MASK = i%32; ( i % 32 = i & 0000 0000 0000 0000 0000 0000 0001 1111 )
i % 32的操作相当于只保留后面5位,此处的0000 0000 0000 0000 0000 0000 0001 和上面的意义不同,它就是32, 即 i % 32 = i & 31 = i & 0x1F.


STL实现

STL 提供了bitset接口,用来存储bit位信息。

template <size_t N> class bitset;/*The size of a bitset is fixed at compile-time (determined by its template parameter). */

所以,初始化的时候需要指定一个数字参数,不是类型参数。因为compile time要确定大小。

代码

#include <iostream>#include <bitset>int main( void ){    const int N = 16;    std::bitset<N> bit; // 编译是指定大小,默认置0    int val = 0;    while( std::cin >> val ){        bit.set(val);    }    for( int i = 0 ; i < N; ++i ){        if( bit.test(i) ) std::cout << i << std::endl;    }    return 0;}

std::bitset< N >再学习

1.上一目说的是bitmap的事情,我们讲道bitset可以用来实现bitmap。但是,bitset不只是只有这一个功能。

bit定义:
A bitset stores bits (elements with only two possible values: 0 or 1, true or false, …).

从上面定义,我们看到,bitset只是用来存储bit,没有说只能是应用与bitmap。
bitmap可以用bitset实现。但是,bitset也有别的作用。

看下面这段代码,使用bitset的构造函数可以直接得到value的二进制位。

Integral value whose bits are copied to the bitset positions.

// constructing bitsets#include <iostream>       // std::cout#include <string>         // std::string#include <bitset>         // std::bitsetint main (){  std::bitset<16> foo;  std::bitset<16> bar (0xfa2);  std::bitset<16> baz (std::string("0101111001"));  std::cout << "foo: " << foo << '\n';  std::cout << "bar: " << bar << '\n';  std::cout << "baz: " << baz << '\n';  return 0;}

总结

  1. bitset可以用来实现bitmap, 当然,他们的参数也表达了不同的逻辑意义。此时std::bitset< N >, N表示,只能映射[ 0, N - 1 )区间的数可以得到映射,也就是映射到N个bit位。此时主要用到, set, reset, test这三个方法。
  2. bitset可以用来实现bitarr,此时,主要是表达value的二进制存储,此时的N代表二进制存储位数,整个bitarr存储的是一个数子而已,只不过存储了全部的二进制bit.但是,上面的方法则不是,它存储的是N个数的映射。此时,主要用的是构造函数,to_ulong, to_string这三个方法。

代码

#include <iostream>#include <bitset>#include <string>void usage1(); // bitmap用法void usage2(); // bitarr用法int main(){    //usage1();    usage2();    return 0;}void usage1(){    const int N = 32;    std::bitset<N> bitmap; // [0, 31)    for( int i = 1; i < 16; ++i ){ // 1 - 15        bitmap.set( i );    }    for( int i = 0; i < N; ++i ){        if( bitmap.test(i) )            std::cout << i << " : " << "yes" << std::endl;        else                std::cout << i << " : " << "no" << std::endl;    }    for( int i = 1; i < 16; ++i ){ // 1 - 15        bitmap.reset( i );    }    for( int i = 0; i < N; ++i ){        if( bitmap.test(i) )            std::cout << i << " : " << "yes" << std::endl;        else                std::cout << i << " : " << "no" << std::endl;    }    //bitmap.set(N);}void usage2(){    const int N = 32;    int val = 2;    std::bitset<N> bitarr1(val);    std::cout << bitarr1 << std::endl;    std::string s = "1111";    std::bitset<N> bitarr2(s);    std::cout << bitarr2 << std::endl;    int ret = bitarr1.to_ulong();    std::cout << ret << std::endl;    ret = bitarr2.to_ulong();    std::cout << ret << std::endl;    std::string str = bitarr1.to_string();    std::cout << str << std::endl;    str = bitarr2.to_string();    std::cout << str << std::endl;}