位图
位图概念
1.
面试题
给
40
亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这
40
亿个数中。【腾讯】
1.
遍历,时间复杂度
O(N)
2.
排序
(O(NlogN))
,利用二分查找
: logN
3.
位图解决
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比
特位来代表数据是否存在的信息,如果二进制比特位为
1
,代表存在,为
0
代表不存在。比如:
![](https://img-blog.csdnimg.cn/3c357d7e1cec46f3b7f6f6fda8908ce7.png)
2.
位图概念
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。
#pragma once
#include<iostream>
#include<vector>
namespace MySTL
{
template<size_t N>
class bitset
{
public:
bitset()
{
_bits.resize(N / 8 + 1, 0);
}
void set(size_t X)
{
size_t i = X / 8;
size_t j = X % 8;
_bits[i] |= (1 << j);
}
void unset(size_t X)
{
size_t i = X / 8;
size_t j = X % 8;
_bits[i] &= (~(1 << j));
}
bool test(size_t X)
{
size_t i = X / 8;
size_t j = X % 8;
return _bits[i] & (1 << j);
}
private:
std::vector<char> _bits;
};
void test_bitset()
{
/*bitset<100> bs;
int array[] = { 1,2,3,4,5,6,7,8,99,78 };
for (const auto& e : array)
{
bs.set(e);
}
std::cout<<bs.test(2)<<std::endl;
std::cout<<bs.test(99)<<std::endl;
std::cout<<bs.test(78)<<std::endl;
std::cout<<bs.test(4)<<std::endl;
bs.unset(2);
bs.unset(99);
bs.unset(78);
bs.unset(4);
std::cout << bs.test(2) << std::endl;
std::cout << bs.test(99) << std::endl;
std::cout << bs.test(78) << std::endl;
std::cout << bs.test(4) << std::endl;*/
bitset<-1> bs;
}
}
位图应用
1.
给定
100
亿个整数,设计算法找到只出现一次的整数?
2.
给两个文件,分别有
100
亿个整数,我们只有
1G
内存,如何找到两个文件交集?
3.
位图应用变形:
1
个文件有
100
亿个
int
,
1G
内存,设计算法找到出现次数不超过
2
次的所有整数
问题1:用两个位图,两个位图可以为00 01 10
当一个数第一个位图为0,第二个位图为1,则表明该数只出现一次。
template<size_t N>
class TwoBitSet
{
public:
void set(size_t x)
{
if (_bs1.test(x) == false && _bs2.test(x) == false) //00 -> 01
{
_bs2.set(x);
}
else if (_bs1.test(x) == false && _bs2.test(x)) //01 -> 10
{
_bs1.set(x);
_bs2.unset(x);
}
}
void PrintNumOnce()
{
for (size_t i = 0; i < N; ++i)
{
if (!_bs1.test(i) && _bs2.test(i))
{
std::cout << i << std::endl;
}
}
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};
void TwoBitSetTest()
{
int array[] = { 1,1,2,3,4,5,6,6,7,7,8,8,8,9,9,9,9 };
TwoBitSet<100> bs;
for (const auto& e : array)
{
bs.set(e);
}
bs.PrintNumOnce();
}
}
问题2:
思路一:一个文件中的整数,set到一个位图,读取第二个文件中的整数判断在不在位图,在就是交集,不在就不是交集。
void TestFindTest()
{
int array1[] = { 1,5,99,6,6,7,10 };
int array2[] = { 1,8,9,6,7,30,10,40,10,40};
bitset<100> bs;
for (const auto e : array1)
{
bs.set(e);
}
for (const auto e : array2)
{
if (bs.test(e))
{
std::cout << e << std::endl;
}
}
}
但是该方法有缺陷:就是第一个文件中的数重复的数会被找出来,还需要去重。
思路二:
一个文件的整数,set到一个位图bs1,另一个文件的整数,set到bs2.
a.遍历bs2中的值,看在不在bs1中,在就是交集。
void TestFindTest()
{
int array1[] = { 1,5,99,6,6,7,10 };
int array2[] = { 1,8,9,6,7,30,10,40,10,40 };
bitset<100> bs1;
bitset<100> bs2;
for (const auto e : array1)
{
bs1.set(e);
}
for (const auto e : array2)
{
bs2.set(e);
}
for (size_t i = 0; i < 100; ++i)
{
if (bs1.test(i) && bs2.test(i))
{
std::cout << i << std::endl;
}
}
}
b.两个位图想与,与完是1的位置的值,就是交集。
问题3:
出现1次 00
出现2次 01
出现3次 10
出现4次 11
template<size_t N>
class TwoBitSet
{
public:
void set(size_t x)
{
if (_bs1.test(x) == false && _bs2.test(x) == false) //00 -> 01
{
_bs2.set(x);
}
else if (_bs1.test(x) == false && _bs2.test(x)) //01 -> 10
{
_bs1.set(x);
_bs2.unset(x);
}
else if (_bs1.test(x) && _bs2.test(x) == false) // 10 -> 11
{
_bs1.set(x);
_bs2.set(x);
}
}
void PrintNumOnce()
{
for (size_t i = 0; i < N; ++i)
{
if (_bs1.test(i))
{
std::cout << i << std::endl;
}
}
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};
void TwoBitSetTest()
{
int array[] = { 1,1,2,3,4,5,6,6,7,7,8,8,8,9,9,9,9 };
TwoBitSet<100> bs;
for (const auto& e : array)
{
bs.set(e);
}
bs.PrintNumOnce();
}