问题描述:
输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=10**7。如果在输入文件中有任何整数重复出现就是致命错误。没有其他数据与该整数相关联。
输出:按升序排列的输入整数的列表。
约束:最多有(大约)1MB的内存空间可用,用充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要优化了。
算法思想: 用位图或者位向量表示集合。
例如可以用一个20位长的字符串来表示一个所有元素都小于20的简单非负整数集合。可以用如下字符串表示集合{1,2,3,5,8,13};
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0
在上述问题中,7位十进制整数表示一个小于1000万的整数,可以使用一个具有1000万个位的字符串来表示这个文件,其中,当且仅当整数i在文件中存在时,第i位为1.
位图法
//伪代码
//初始化集合
for i = [0,n)
bit[i] =0
//将出现的元素插入到集合中
for each i in the input file
//输入排序了的元素
for i = [0,n)
if bit[i] == 1
write i on the out put file
位向量法
#define BITSPEWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1+(N-1)/BITSPEWORD]
//书中为a[1+N/BITSPEWORD] 当N能被BITSPEWORD整除时,会浪费1个int的空间
//不被整除时,最多是最后一个int的空间没有被完全利用而已
void set (int i)
{
// i >> SHIFT 将 i 定位到哪一个 int 中去 实际上等于 i / 32
// i & MASK 只保留了i 的 0 至 4 位,相当于 i % 32 ,然后把 1 左移这么多位,定位到相应的 int 中所对应的那一位
a[ i >> SHIFT ] |= (1<<( i & MASK) ) ;//位设置
}
void clr(int i)
{
a[ i >> SHIFT ] &= ~ (1<<( i & MASK) ) ;//清零
}
int test (int i)
{
return a[ i >> SHIFT ] & (1<<( i & MASK) ) ;//同1做与,结果为0则原位为0,不为0则原位为1
}