《编程珠玑》第一章提到了一个排序问题,具体需求抄在下面:
输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=107。如果在输入文件中有任何整数重复出现就是致命错误。没有其他数据与该整数相关联。
输出:按升序排列的输入整数的列表。
约束:最多有(大约)1MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要进一步优化了。
整个解题的思路是通过位向量来进行排序,使用107个位来代表从0~107的数,如果文件中包含数n,则将第n位置为1,输出的时候按序遍历每一位,如果该位为1就输出该位的序号,即可得到最后的排序序列。
书中实现位向量是通过整数数组来实现的,或许是因为c语言中没有位向量这个数据结构,c++中的标准模板库已经提供了bitset这个数据结构。实现的原理如下:
- 由于一个整型是32位,所以只需定义一个长度为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
代码如下
//一个整型有32位
#define BITSPERWORD 32
//定义移位的长度,2的5次方为32
#define SHIFT 5
//定义取模的掩码
#define MASK 0x1F
//输入文件中整数的最大范围
#define N 10000000
//定义存储的数组
int a[1 + N/BITSPERWORD];
/*
*根据参数i,设置对应的标志位的值
*i>>SHIFT,i右移5位,相当于i/32
*i & MASK,i和11111做与运算,得到i%32
*1<<(i & MASK),1左移(i%32)位,将结果与a[i>>SHIFT]做或运算
*即可设置整数i对应的标志位为1
*/
void set(int i)
{
a[i>>SHIFT] |= (1<<(i & MASK));
}
/*
*根据参数i,设置对应的标志位为0
*/
void clr(int i)
{
a[i>>SHIFT] &= ~(1<<(i & MASK));
}
/*
*根据参数i,判断对应的标志位是否为1
*/
int test(int i)
{
return a[i>>SHIFT] & (1<<(i & MASK));
}