bitmap学习
来源:互联网 发布:java求3个最小公倍数 编辑:程序博客网 时间:2024/06/06 00:17
主要参考了以下两篇文章
[《编程珠玑》学习总结1—bitmap]
[浅谈bitmap算法]
基本概念
- 什么是bitmap
我自己的理解是:首先是一种map,其次映射到bit位。关键还是它是一种映射,映射到的value由于是bit,比较节省空间。bitmap是一种映射,每一个bit位用来表示key所对应的value是否存在。 - 优点和缺点是什么
优点:节省空间,并且,位运算比较快
缺点:不能处理重复元素。
思路
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都是存在的。关键就是对于一个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;}
总结
- bitset可以用来实现bitmap, 当然,他们的参数也表达了不同的逻辑意义。此时std::bitset< N >, N表示,只能映射[ 0, N - 1 )区间的数可以得到映射,也就是映射到N个bit位。此时主要用到, set, reset, test这三个方法。
- 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;}
- BitMap 学习
- bitmap学习
- bitmap学习
- android bitmap学习
- 位图Bitmap学习1
- android Bitmap学习总结
- android Bitmap学习总结
- Bitmap.Option 学习
- android Bitmap学习总结
- android bitmap学习资料
- android Bitmap学习总结
- Android学习之Bitmap
- android Bitmap学习总结
- android Bitmap学习总结
- android Bitmap学习总结
- android Bitmap学习总结
- android Bitmap学习总结
- Bitmap.createBitmap学习笔记
- 独善其身是国人低劣的本质
- Listener学习笔记-- day02 邮件系统
- ElasticSearch 2.4.0系列之一 es简介
- ConstraintLayout的使用介绍
- 装饰设计模式
- bitmap学习
- 最小循环表示
- html标签相关
- 将QMessageBox的Yes和No按钮翻译成中文
- 【Oracle】windows平台oracle11g安装与简单配置
- 扩增子分析QIIME. 3以管理员安装QIIME1.9.1至Ubuntu16.04
- Git-管理版本
- 存储系统 (二)
- unity中Mathf.Lerp、Mathf.MoveTowards、Mathf.SmoothStep、Mathf.SmoothDamp的区别