场景
现有40亿个不重复无符号整数,未排序。在其中检测一个给定无符号整数是否存在。
40亿数据总字节大小大概为15G,无法一次加载入内存中
尝试方法:
- 遍历 --------时间复杂度O(N)
- 先排序 O(NlogN),再二分查找:O(logN)
- 哈希切割-------查找效率不高
- 使用位图解决-----查找效率最高,最省空间
位图解决的思路
数据是否保存在40亿条数据当中,有存在/不存在两种状态,可以使用二进制1/0在比特位中进行标记。
40亿条数据为无符号整形,其最大取值范围为0到0xffffffff(2^23个无符号整形数据),若每个数字对应一个比特位,则只需要512兆存储空间,大大节省了查找时占用的空间资源。
我们可以通过C++中的bitset类来实现
#include<bitset>
size_t array[] = { 1, 6, 40, 23, 11, 12, 13 };
std::bitset<512> bt;//<给出的比特位数> 512*2^20
for (auto e : array){ //将数组中数据遍历统计,相应比特位置1
bt.set(e);
}
std::cout << "count: " << bt.count() << std::endl;//打印比特位为1位的总数
if (bt.test(6))//查找6是否存在(6所在的对应比特位是否为1)
std::cout << "6 can find" << std::endl;
bt.reset(23);//删除23
也可以自己模拟bitset来实现,理解位图思想
自定义的bitset类中,给定两个成员变量,一个是char类型的数组 _bit 用于保存每个无符号整数是否存在,_bit中一个元素占8个比特位。在保存数据data时,data除8代表在的数组的第几个元素,data模8代表其在该元素比特位的第几位。
//自定义的位操作
namespace mybitset
{
template <size_t N> //N为位数
class bitset{
public:
bitset()
:_bit(N/8+1)
{}
//比特位为1的总数
size_t count(){
return _count;
}
//比特位总数
size_t size(){
return N;
}
//将pos位置置1
void set(size_t pos){
assert(pos < N);
size_t _byte = pos / 8; //哪个字节
size_t bit = pos % 8; //哪个比特位
_bit[_byte] |= (1 << bit); //1向左移bit位
_count++;
}
//将pos位置置0
void reset(size_t pos){
assert(pos < N);
size_t _byte = pos / 8; //哪个字节
size_t bit = pos % 8; //哪个比特位
//1100 1010
//0000 1000 1左移bit位
_bit[_byte] &= (~(1 << bit)); //1111 0111 取反
_count--;
}
//检测pos位置比特位是否为1
bool test(size_t pos){
assert(pos < N);
size_t _byte = pos / 8; //哪个字节
size_t bit = pos % 8; //哪个比特位
//1100 1010
if (_bit[_byte] &= (1 << bit)){ //0000 1000
return true;
}
return false;
}
private:
std::vector<char> _bit; //一个char占一个字节(8个比特位)
size_t _count; //比特位为1的总数
};
}