技术分享2026-08-21蔡明思
位图的原理及实现
我们可以使用boolean[] visited 这样的布尔数组,来记录已经被访问过的元素。
// 假设 nums 是一个包含 1000 个整数的数组
int[] nums = {...}
// 我们在写算法时
// 可能会用一个布尔数组来记录 nums 中那些元素已经被访问过
boolean[] visited = new boolean[nums.length];
visited[10] = true;
visited[100] = true;
但其实还是有更狠的优化方式,布尔类型我们都知道他是有false 和 true两种状态的。所以其实通过0和1就可以表示这两个意思,大部分的编程语言中在内存中一个布尔类型的值一般为一个字节(byte),也就是8 bit。所以,编程语言内置的布尔数组实际上只需要1/8的位置来达到这个visited数组的效果。所以我们优化的方式也就很明显了,通过比特位来表示是否访问过。
其实在实际算法题中,直接使用布尔数组就可以了,这种位图只是在一些特定的题目以及要求下才使用的一种结构,在处理超大规模的数据时可能才需要用到这个结构。
位图的原理和实现
我们可以使用long[] words来作为存储数组。就是把索引的方式更改一下。我们可以知道一个long类型的数据是8个字节,就是64bit (8*8),假设我们需要第135个比特位,那么:
1、目标比特位在words数组的第几个元素? 135/64=2 所以在words[2]这个元素里。
2、在对应元素的第几个比特位呢? 135%64 = 7,得到在第七位。
所以我们的目标比特位就在words[2]的元素里的第七个bite位。
你可以把它幻想成一个二维数组,每一行是一个元素,每个元素里有64个bite位,那这么看的话我们就 可以通过几个方法让用户可以访问这个对应bite位:
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;
class MyBitSet {
private:
// 使用 unsigned long 数组作为位图的底层存储
vector<unsigned long long> words;
// 位图能够存储的最大元素值 + 1
int size;
public:
MyBitSet(int size) : size(size) {
// 根据 size 计算需要多少个 unsigned long long 来存储
int arraySize = size / 64 + 1;
words.resize(arraySize, 0);
}
// 判断指定比特位是否为 1
bool get(int bitIndex) {
if (bitIndex < 0 || bitIndex >= size) {
throw out_of_range("bitIndex must be between 0 and " + to_string(size - 1));
}
// 找到 bitIndex 在 words 数组中的索引
int wordIndex = bitIndex / 64;
// 找到 bitIndex 在 unsigned long long 值中的具体 bit 位
int bitOffset = bitIndex % 64;
// 使用 & 操作判断该位是否为 1
return (words[wordIndex] & (1ULL << bitOffset)) != 0;
}
// 将指定比特位设置为 1
void set(int bitIndex) {
if (bitIndex < 0 || bitIndex >= size) {
throw out_of_range("bitIndex must be between 0 and " + to_string(size - 1));
}
// 找到 bitIndex 在 words 数组中的索引
int wordIndex = bitIndex / 64;
// 找到 bitIndex 在 unsigned long long 值中的具体 bit 位
int bitOffset = bitIndex % 64;
// 使用 | 操作将该位置 1
words[wordIndex] |= (1ULL << bitOffset);
}
// 将指定比特位设置为 0
void clear(int bitIndex) {
if (bitIndex < 0 || bitIndex >= size) {
throw out_of_range("bitIndex must be between 0 and " + to_string(size - 1));
}
int wordIndex = bitIndex / 64;
int bitOffset = bitIndex % 64;
// 使用 & 和 ~ 操作将该位置 0
words[wordIndex] &= ~(1ULL << bitOffset);
}
};
int main() {
MyBitSet bitSet(1000);
bitSet.set(10);
bitSet.set(100);
bitSet.set(500);
cout << "Get 10: " << (bitSet.get(10) ? "true" : "false") << endl; // true
cout << "Get 100: " << (bitSet.get(100) ? "true" : "false") << endl; // true
cout << "Get 200: " << (bitSet.get(200) ? "true" : "false") << endl; // false
bitSet.clear(100);
cout << "Get 100 after clear: " << (bitSet.get(100) ? "true" : "false") << endl; // false
return 0;
}