不重复整数的查找判断
问题是这样:假设集合A中有 n 个整数,这些整数不重复,现给定一个数 i ,判断是否在A中。
最容易想到的是将n个整数存入文件或内存中,然后顺序读取与 i 比较,但是如果n很大,那么这个过程就会花费很长的时间(和空间),因此需要一种更简洁有效的手段。
再来看看信息:整数、不重复,也就是说每个整数的状态只有2种:存在或不存在,不用记录整数出现的次数。想到这里,我想起信号集的表示 sigset_t, sigset_t表示的信号也只有2种状态,sigset_t用第 i 位为1表示有第 i 个信号,其本质是一个超大的整数。
因此可不可以用这种方法表示集合A呢?
比如,定义一个bit位的集合,bit[n],用 bit[i]=0 表示整数 i 不存在,用bit[i]=1 表示整数 i 存在,
假设是32位机器,那么1B可以表示32个整数,1KB可以表示1024*32个整数,1MB就可以表示1024*1024*32=33 554 432个整数,可以看到用很小的空间开销就解决了存储问题。
那么查询效率如何呢?显然查找 i 只需取bit[i]的值即可,查询效率为常数级。所以这种方法是可行且高效的。
接下来问题来了,如何做这个bit[n]呢?在C中并没有bit的基本类型,常用的整数类型为int(32位),那么考虑将int看成32个bit的集合,
用 arr[1+n/32] 来作为存储n个整数的集合bit[n]。
接下来是将第 i 个bit位置0或1的问题,涉及到bit位的,一般都是采用位操作的办法。以下是参照《编程珠玑》中的实现:可以看到全部由位操作实现,运行效率是很高的。
void set_bit(int i){ arr[ i >> 5 ] |= ( 1 << ( i & 0x1F )); /*这里 i>>5 即 i/32,判断i在arr[]的哪一块中, 找到该块后,需要将该块第 i%32 位置1(其他位不变),所以用 | 操作, (i & 0x1F) 就是求 i%32,1<<()即将第 (i&32) 位置1,其余位为0;*/ } /*同理*/ void clear_bit(int i){ arr[ i >> 5 ] &= ( 1 << ( i & 0x1F )); /*将第i位清0;*/ } int check_bit(int i ){ return arr[ i >> 5 ] & ( 1 << ( i & 0x1F )); //查询第i位的值; }