在本文中, 我们来介绍一下bit-map,你可别以为这是什么bitmap图像, 那本文要介绍的bit-map是什么呢? 且听我慢慢道来。
坐过火车没? 当你在火车上, 想拉屎的时候, 你得去上厕所啊。 屁颠屁颠爬到厕所处, 发现上面亮了一个灯, 表示里面有人, 你郁闷至极, 但无可奈何。 过了很久, 那个人出来了, 那个灯也就熄灭了, 你就可以上厕所了。
这个灯, 就是计算机中的一个bit, 有两种状态 , 1表示有人在厕所中, 0表示没有人在厕所中。 所以, 从这个意义上说, 一个bit可以标志某事物的一个状态, 当这个bit与某事物状态建立映射后, 我说们, 形成了一个bit到事物状态的map.
我们知道, 一个unsign char有8bit, 也就是说, 一个无符号字符可以标志某个厕所的8个坑位的状态, 下面我们看看程序:
#include <iostream> using namespace std; int main(void) { unsigned char c; // 8个坑位都没有人 c = 0; // 8个坑位都有人 c = 255; // 第1个(也可以认为是第0个)坑位有人 c = 128; // 最后两个坑位有人 c = 3; return 0; }看到没? 一个bit标志一个坑, 标志一个事物的状态, 那么一个unsigned char标志着8个事物的状态。 同理, 一个unsigned int标志着32个事物的状态, 其实一个int也可以标志着32个事物的状态。 (当然, 这里所说的状态都是二值状态)
现在, 假设有N个事物状态, 那至少需要多少个int来表示呢? 很显然是 N/32 + 1. 假设有100个事物(N=100), 那至少需要4个int (总共128位). 说的有点多了, 直接看程序吧:
#include <iostream> using namespace std; #define BIT_INT 32 // 1个int可以标志32个坑 #define SHIFT 5 #define MASK 0x1f #define N 100 int a[1 + N / BIT_INT]; // 需要1 + N / BIT_INT 个整数来标志N个事物 // 将所有位都初始化为0状态 void setAllZero() { memset(a, 0, (1 + N / BIT_INT) * sizeof(int)); } // 设置第i位为1, 表示有人在厕所里面 void setOne(int i) { a[i >> SHIFT] |= (1 << (i & MASK)); } // 设置第i位为1, 表示没有人在厕所里面 void setZero(int i) { a[i >> SHIFT] &= ~(1 << (i & MASK)); } // 检查第i位的值, 看看有没有人在厕所里面 int getState(int i) { return (a[i >> SHIFT] & (1 << (i & MASK))) && 1; } int main(void) { int bitNumber = (1 + N / BIT_INT) * BIT_INT; cout << bitNumber << endl; setAllZero(); int i = 0; for(i = 0; i < bitNumber; i++) { cout << getState(i); } cout << endl; setOne(0); setOne(1); setOne(2); setOne(3); setOne(4); for(i = 0; i < bitNumber; i++) { cout << getState(i); } cout << endl; setZero(0); setZero(1); setZero(2); for(i = 0; i < bitNumber; i++) { cout << getState(i); } cout << endl; return 0; }看看结果吧:
128
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
果然, 结果与预期符合。
我们再看程序:
#include <iostream> #include <set> using namespace std; #define BIT_INT 32 // 1个int可以标志32个坑 #define SHIFT 5 #define MASK 0x1f #define N 100 int a[1 + N / BIT_INT]; // 需要1 + N / BIT_INT 个整数来标志N个事物 // 产生伪随机数 unsigned int getRandom() { return rand(); } // 将所有位都初始化为0状态 void setAllZero() { memset(a, 0, (1 + N / BIT_INT) * sizeof(int)); } // 设置第i位为1, 表示有人在厕所里面 void setOne(int i) { a[i >> SHIFT] |= (1 << (i & MASK)); } // 设置第i位为1, 表示没有人在厕所里面 void setZero(int i) { a[i >> SHIFT] &= ~(1 << (i & MASK)); } // 检查第i位的值, 看看有没有人在厕所里面 int getState(int i) { return (a[i >> SHIFT] & (1 << (i & MASK))) && 1; } int main(void) { int r = N; int size = 40; // 假设有40个事物(数字), 他们在[0, N-1]这个区间内 // 构造40个互不相等的事物, 实际上, s中的元素是有序的, 但在该程序中, 我们不需要太关注这个 set<int> s; while(size != s.size()) { s.insert(getRandom() % r); } // 将所有坑位的人赶出来, 初始化 setAllZero(); set<int>::iterator it; for(it = s.begin(); it != s.end(); it++) { setOne(*it); // 将事物*it与其对应的坑位*it联系起来, 当*it这个事物存在时, 对应的坑位*it的状态为1 cout << *it << " "; } cout << endl; int i = 0; int bitNumber = (1 + N / BIT_INT) * BIT_INT; for(i = 0; i < bitNumber; i++) { cout << i << "对应的坑位状态为:--->" << getState(i) << endl; // 获取坑位状态 } cout << endl; return 0; }
结果为:
0 2 3 4 5 11 12 16 18 21 22 24 26 27 33 34 35 36 38 41 42 45 47 53 58 61 62 64 67 69 71 73 78 81 82 91 92 94 95 99
0对应的坑位状态为:--->1
1对应的坑位状态为:--->0
2对应的坑位状态为:--->1
3对应的坑位状态为:--->1
4对应的坑位状态为:--->1
5对应的坑位状态为:--->1
6对应的坑位状态为:--->0
7对应的坑位状态为:--->0
8对应的坑位状态为:--->0
9对应的坑位状态为:--->0
10对应的坑位状态为:--->0
11对应的坑位状态为:--->1
12对应的坑位状态为:--->1
13对应的坑位状态为:--->0
14对应的坑位状态为:--->0
15对应的坑位状态为:--->0
16对应的坑位状态为:--->1
17对应的坑位状态为:--->0
18对应的坑位状态为:--->1
19对应的坑位状态为:--->0
20对应的坑位状态为:--->0
21对应的坑位状态为:--->1
22对应的坑位状态为:--->1
23对应的坑位状态为:--->0
24对应的坑位状态为:--->1
25对应的坑位状态为:--->0
26对应的坑位状态为:--->1
27对应的坑位状态为:--->1
28对应的坑位状态为:--->0
29对应的坑位状态为:--->0
30对应的坑位状态为:--->0
31对应的坑位状态为:--->0
32对应的坑位状态为:--->0
33对应的坑位状态为:--->1
34对应的坑位状态为:--->1
35对应的坑位状态为:--->1
36对应的坑位状态为:--->1
37对应的坑位状态为:--->0
38对应的坑位状态为:--->1
39对应的坑位状态为:--->0
40对应的坑位状态为:--->0
41对应的坑位状态为:--->1
42对应的坑位状态为:--->1
43对应的坑位状态为:--->0
44对应的坑位状态为:--->0
45对应的坑位状态为:--->1
46对应的坑位状态为:--->0
47对应的坑位状态为:--->1
48对应的坑位状态为:--->0
49对应的坑位状态为:--->0
50对应的坑位状态为:--->0
51对应的坑位状态为:--->0
52对应的坑位状态为:--->0
53对应的坑位状态为:--->1
54对应的坑位状态为:--->0
55对应的坑位状态为:--->0
56对应的坑位状态为:--->0
57对应的坑位状态为:--->0
58对应的坑位状态为:--->1
59对应的坑位状态为:--->0
60对应的坑位状态为:--->0
61对应的坑位状态为:--->1
62对应的坑位状态为:--->1
63对应的坑位状态为:--->0
64对应的坑位状态为:--->1
65对应的坑位状态为:--->0
66对应的坑位状态为:--->0
67对应的坑位状态为:--->1
68对应的坑位状态为:--->0
69对应的坑位状态为:--->1
70对应的坑位状态为:--->0
71对应的坑位状态为:--->1
72对应的坑位状态为:--->0
73对应的坑位状态为:--->1
74对应的坑位状态为:--->0
75对应的坑位状态为:--->0
76对应的坑位状态为:--->0
77对应的坑位状态为:--->0
78对应的坑位状态为:--->1
79对应的坑位状态为:--->0
80对应的坑位状态为:--->0
81对应的坑位状态为:--->1
82对应的坑位状态为:--->1
83对应的坑位状态为:--->0
84对应的坑位状态为:--->0
85对应的坑位状态为:--->0
86对应的坑位状态为:--->0
87对应的坑位状态为:--->0
88对应的坑位状态为:--->0
89对应的坑位状态为:--->0
90对应的坑位状态为:--->0
91对应的坑位状态为:--->1
92对应的坑位状态为:--->1
93对应的坑位状态为:--->0
94对应的坑位状态为:--->1
95对应的坑位状态为:--->1
96对应的坑位状态为:--->0
97对应的坑位状态为:--->0
98对应的坑位状态为:--->0
99对应的坑位状态为:--->1
100对应的坑位状态为:--->0
101对应的坑位状态为:--->0
102对应的坑位状态为:--->0
103对应的坑位状态为:--->0
104对应的坑位状态为:--->0
105对应的坑位状态为:--->0
106对应的坑位状态为:--->0
107对应的坑位状态为:--->0
108对应的坑位状态为:--->0
109对应的坑位状态为:--->0
110对应的坑位状态为:--->0
111对应的坑位状态为:--->0
112对应的坑位状态为:--->0
113对应的坑位状态为:--->0
114对应的坑位状态为:--->0
115对应的坑位状态为:--->0
116对应的坑位状态为:--->0
117对应的坑位状态为:--->0
118对应的坑位状态为:--->0
119对应的坑位状态为:--->0
120对应的坑位状态为:--->0
121对应的坑位状态为:--->0
122对应的坑位状态为:--->0
123对应的坑位状态为:--->0
124对应的坑位状态为:--->0
125对应的坑位状态为:--->0
126对应的坑位状态为:--->0
127对应的坑位状态为:--->0
现在应该完成清楚了bit-map吧, 所谓bit-map, 实际上就是用一个bit去map一个事物的状态(二值状态). bit-map的好处是: 节省空间。
实际上, bit-map在大数据处理中经常会用到, 一些笔试面试题经常考, 后续我们会陆续介绍到。