Bitmap(位图)是一种使用位(bit)来表示元素是否存在的数据结构,特别适合大规模整数去重统计,内存占用极低。
## Bitmap类实现
```cpp
#include
#include
#include
class Bitmap {
private:
std::vector
size_t numBits;
public:
// 构造函数,创建能容纳maxNum个数的bitmap
Bitmap(size_t maxNum) : numBits(maxNum) {
// 每个字节8位,向上取整计算需要的字节数
bits.resize((maxNum + 7) / 8, 0);
}
// 设置某个位为1
void set(size_t pos) {
if (pos >= numBits) return;
bits[pos / 8] |= (1 << (pos % 8));
}
// 检查某个位是否为1
bool test(size_t pos) const {
if (pos >= numBits) return false;
return (bits[pos / 8] & (1 << (pos % 8))) != 0;
}
// 清除某个位(设为0)
void clear(size_t pos) {
if (pos >= numBits) return;
bits[pos / 8] &= ~(1 << (pos % 8));
}
// 统计设置为1的位数(不同元素的数量)
size_t count() const {
size_t sum = 0;
for (size_t i = 0; i < numBits; ++i) {
if (test(i)) {
sum++;
}
}
return sum;
}
// 更高效的统计1的位数的方法
size_t countEfficient() const {
static const unsigned char bitCounts[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
size_t sum = 0;
// 计算完整字节
size_t completeBytes = numBits / 8;
for (size_t i = 0; i < completeBytes; ++i) {
sum += bitCounts[bits[i]];
}
// 处理剩余不足一个字节的位
size_t remainingBits = numBits % 8;
if (remainingBits > 0) {
unsigned char mask = (1 << remainingBits) - 1;
sum += bitCounts[bits[completeBytes] & mask];
}
return sum;
}
};
```
## 使用示例:整数去重统计
```cpp
#include
#include
#include
#include
#include
int main() {
const size_t MAX_VALUE = 10000000; // 可能出现的最大整数值
const size_t DATA_SIZE = 5000000; // 测试数据量
// 生成随机数据
std::vector
data.reserve(DATA_SIZE);
std::srand(std::time(nullptr));
for (size_t i = 0; i < DATA_SIZE; ++i) {
data.push_back(std::rand() % MAX_VALUE);
}
// 方法1:使用Bitmap进行去重统计
clock_t start = clock();
Bitmap bitmap(MAX_VALUE);
for (int num : data) {
bitmap.set(num);
}
size_t uniqueCount = bitmap.countEfficient();
clock_t end = clock();
std::cout << "Bitmap统计结果: 共有" << uniqueCount << "个不同的整数" << std::endl;
std::cout << "Bitmap耗时: " << double(end - start) / CLOCKS_PER_SEC << "秒" << std::endl;
// 方法2:使用unordered_set进行去重统计(对比)
start = clock();
std::unordered_set
for (int num : data) {
numSet.insert(num);
}
end = clock();
std::cout << "Set统计结果: 共有" << numSet.size() << "个不同的整数" << std::endl;
std::cout << "Set耗时: " << double(end - start) / CLOCKS_PER_SEC << "秒" << std::endl;
return 0;
}
```
## Bitmap优势
1. **空间效率高**:每个整数只占用1个bit位,比如1000万个不同整数,只需约1.2MB内存
2. **去重自动完成**:同一个整数多次set操作,最终在bitmap中只占一个位
3. **统计高效**:对于有限范围的整数,统计速度快,内存占用少
## 使用场景
1. 大规模整数去重统计(如统计网站UV)
2. 布隆过滤器的底层实现
3. 需要节省内存的场景下进行集合运算
## 局限性
1. 只适用于非负整数
2. 需要预先知道数据的最大值
3. 不保存原始数据,只记录数据是否存在
如需处理有符号整数或更复杂的数据类型,需要进行适当的映射或扩展实现。