面试收集小技巧之位图实现
之前有很多题目说到要用位图来解决问题,比如,
1.找1到10W之间没有出现的两个数,其中这些数是乱序排放(可能数字比10W还大,比如说40亿)
2.一个最多包含n个正整数的文件,每个数都小于n,其中n=107。如果在输入文件中有任何正数重复出现就是致命错误。没有其他数据与该正数相关联。输出按升序排列的输入正数的列表
3.给出40亿个数,搜索某一个数是否存在于这些数之中
4.两个整数集合A和B,求交集。
这里面就有位图的用武之地了,位图的好处是一来位操作比较快,二来占用空间会比较小, 比如说10W个整数,如果全部加载到内存的话大概需要105*4B(大约400KB),但是如果知道这些数最大为105的话,只需要105bit(大约10KB),时间效率和空间效率都有了很大提高。
以问题2为例,位图法解题思路如下:
a.分配位图空间,初始每位置零:最多n(n=107)个数,怎么在语言中具体实现这个位图。
b.遍历文件,出现过的整数对应的bit位置为1。
c.顺序遍历位图,为1的bit位的索引按序输出。
这个思路在具体实现时,分配空间怎么分?置位操作怎么实现?
对于c语言,可以用整数或者字符数组来模拟这么一个大的位图,以整数为例,申请一个数组int bitmap[1000]即能表示32000个bit位,因为数组是连续存放的,所以可以用来模拟。因此对于最大值和最小值相差为N(N可能非常大)的状况,需要整数数组的大小是N/32.进行置位操作是,首先通过索引定位到位所在整数的位置(若索引为i,那么整数在整数数组中的位置为i/32即i>>5),然后确定这一位在索引到的整数中的哪一位(i%32,即i&0x1F)。具体操作代码如下:
#define MaxCount 100000int bitmap[1+MaxCount/32];void set(int index){assert(index<MaxCount);assert(index>=0);bitmap[index>>5]|=(1<<(index&0x1F);}void reset(int index){assert(index<MaxCount);assert(index>=0);bitmap[index>>5]&=(~(1<<(index&0x1F)));}
而对于C++,还可以用bitset这一数据结构直接来实现。
#include <iostream>#include<bitset>using namespace std;int main(int argc, char *argv[]){const int max = 10000000;int n,i;bitset<max+1> bit; //初始默认所有二进制位为0while(scanf("%d",&n)!=EOF)
{bit.set(n,1); //将第n位置1
}for(i=0;i<=max+1;i++){if(bit[i]==1)printf("%d ",i);}return 0;}