给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
对于上面这道题大多数人都会想到用遍历和二分查找的方法去解决,但是会存在一个问题,40亿个unsigned int类型的整数,所占内存空间大约在16G左右了,这样大的空间内存当中是放不下的,那么应该怎么办呢?
我们可以用位图来解决这个问题:
我们会发现无符号整数有2^ 32 个,而我们如果以bit位来计量的话,就只需要 2^32个bit位,也就是512MB的空间,内存当中是可以进行存储的,那么我们该如何进行操作呢?
我们需要判断的是一个数在不在,我们就假设存在bit位就是1,不存在bit位就是0,我们以char类型来进行划分,一个char类型数据就占8个bit位,如下图所示:

一个char类型型有8个比特位,因此N个位的位图就需要用到N/8个,但是实际我们所需的整型个数是N/8+1,因为所给非类型模板参数N的值可能并不是8的整数倍。
例如:N=27,N/8+1 = 3+1 = 4;

template<size_t N>
class bitset
{
public:
//构造函数
bitset()
{
_bits.resize(N / 8 + 1, 0);
}
private:
vector<char> _bits;
};
set成员函数用于设置位,设置位的方法如下:

void set(size_t x)
{
size_t i = x / 8;
size_t j = x % 8;
//计算x位所在的第i个位置所占的第j个bit位
_bits[i] |= (1 << j);
//将该设置为1
}
reset成员函数用于清空位,清空位方法如下:

void reset(size_t x)
{
size_t i = x / 8;
size_t j = x % 8;
//计算x位所在的第i个位置所占的第j个bit位
_bits[i] &= ~(1 << j);
//将该设置为0
}
test成员函数用于获取位的状态,方法如下:

bool test(size_t x)
{
size_t i = x / 8;
size_t j = x % 8;
//计算x位所在的第i个位置所占的第j个bit位
return _bits[i] = (1 << j);
}
我们来看几道题目:
我们可以将整数标记为3种状态:
一个位只能表示两种状态,而要表示三种状态我们至少需要用两个位,因此我们可以开辟两个位图,这两个位图的对应位置分别表示该位置整数的第一个位和第二个位。
我们可以将着三种状态分别定义为00、01、10,此时当我们读取到重复的整数时,就可以让其对应的两个位按照00→01→10的顺序进行变化,最后状态是01的整数就是只出现一次的整数。
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
bool insert1 = bt1.test(x);
bool insert2 = bt2.test(x);
//00
if (insert1 == false && insert2 == false)
{
//->01
bt2.set(x);
}
else if (insert1 == false && insert2 == true)
{
//->10
bt1.set(x);
bt2.reset(x);
}
}
void print_once_num()
{
for (size_t i = 0; i < N; i++)
{
if (bt1.test(i) == false && bt2.test(i) == true)
{
cout << i << endl;
}
}
}
private:
bitset<N> bt1;
bitset<N> bt2;
};
方案一:(一个位图需要512M内存)
方案二:(两个位图刚好需要1G内存,满足要求)
该题跟题目一的做法一样,我们将数据标记为4种状态:
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
bool insert1 = bt1.test(x);
bool insert2 = bt2.test(x);
//00
if (insert1 == false && insert2 == false)
{
//->01
bt2.set(x);
}
else if (insert1 == false && insert2 == true)
{
//->10
bt1.set(x);
bt2.reset(x);
}
else if (insert1 == true && insert2 == true)
{
//->11
bt1.set(x);
bt2.set(x);
}
}
void print_twice_num()
{
for (size_t i = 0; i < N; i++)
{
if ((bt1.test(i) == false && bt2.test(i) == true) || (bt1.test(i) ==true && bt2.test(i) == true))
{
cout << i << endl;
}
}
}
private:
bitset<N> bt1;
bitset<N> bt2;
};