关于位向量的笔记
在《编程珠玑》中提到一个用位向量解决排序的问题,其具体的要求如下:
输入:一个包含最多n个整数的文件,每个整数都小于n,且无重复
输出:按升序排列的输入整数的列表
约束:最多有(大约)1MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要进一步优化了。
基本的思路是这样的:用n个位表示0-n之间的整数,其中位的状态分为0和1两种,0表示这个整数没有出现,1表示出现了。输出时遍历每一位,状态为1则输出该位的序号,即为整数值。不能用一个变量表示一个位,因为这样对于空间的浪费是很严重的,但是可以分配一个int,再将多个int组成一个数组,得到int数组。对于这个数组内的int进行位操作。
因此位向量的实现方法大致的思路是:多个位组成一个基本数据类型,多个基本数据类型组成数组。对于上面这个问题只要找出表示整数的未即可。方法如下:
1 //整数的位数,假设为32 2 #define BitPerWord 32; 3 //定义移位的长度,2的5次方为32 4 #define Shift 5 5 //定义取模的掩码 6 #define Mask 0x1F 7 //定义整数的最大值 8 #define N 100000 9 10 //数组饿大小 11 int intArray[(N-1)/max + 1] 12 /* 13 *根据参数i,设置对应的标志位的值 14 *i>>SHIFT,i右移5位,相当于i/32 15 *i & MASK,i和11111做与运算,得到i%32 16 *1<<(i & MASK),1左移(i%32)位,将结果与a[i>>SHIFT]做或运算 17 *即可设置整数i对应的标志位为1 18 */
举个例子:
所以只需定义一个长度为107/32的整型数组即可提供所需的二进制位,整数数组记为A。 从输入文件中读入一个整数,将这个整数整除32,得到的结果即是表示该数存在的二进制位记录在数组的哪个下标的整数中, 比如111,用111整除32得3,即代表该数存在的二进制位在数组下标为3的数,即A[3]中。 将读入的数模32,得到的数即为代表该数的标志位在A[3]中的位置,比如111模32结果为15, 则将A[3]的第15位置为1,即A[3]现在为00000000000000000100000000000000,即为32768 如此即可使用整型数组模拟位集合,比如读入的第二个数为127,127整除32为3,模32为31,则A[3]为10000000000000000100000000000000
有3种相应的位操作,分别为设置为0,设置为1,读取当前值。以下是具体实现:
//与位移之后的1做或,使得该位置的该位为1 void set(int i){ a[i>>Shift] |= (1<<(i&Mask)); } void clr(int i){a[i>>Shift] &= ~(1<<(i&Mask));} void test(int i){ return a[i>>Shift] & (1<<(i&Mask));}
因此对于上述问题的揭发就是一次读入文件,将出现过的数字所表示的位设为1,之后在遍历所有位,即1-N,将位为1的值输出即是升序排列。