1. 开篇
程序设计的问题:
输入: 所输入的是一个文件,之多包含n个正整数,每个正整数都要小于n,这里 n=10^7。如果输入时某一个整数出现了两次,就会产生一个致命的错误。这些整数与其他任何数据都不关联。
输出:以增序形式输出经过排序的整数列表
约束:至多(大概)只有1MB的可用主存,但是可用磁盘空间非常充足。运行时间至多允许几分钟,10秒钟时间是最适宜的运行时间。
解法1:利用本问题固有的性质,将每个号码存储在7个字节里面,然后对其进行排序。1MB(1000000B)的空间中大约可以存储143000个电话号码。但是如果将每一个号码用32位整数表示,那么1MB的空间可以存储250000个号码。
这样我们可以分40次,分别将 0到249999之间的电话号码,250000到499999之间的电话号码,……,9750000到9999999之间的电话号码排序。
这样读入文件40次就可以完成整个文件的排序了。
但是我们更希望得到一次完全读入,排序,一次输出。但是只有当所有的数都存储在内存中时才可能实现。那么我们是否可以将一千万个不同整数表示在大约八百万可用比特中呢。
解法2:每个整数的7个十进制数字表示了一个小于千万的数字,我们可以使用具有一千万位的位串表示文件。那么当且仅当整数i在该文件中时,第i位才设置为1,。使用本方法需要有三个属性:输入的范围相对小一些,并且不包含重复数据,而且没有数据与单个整数以外的每一个记录相关联。
那么程序可以分三个阶段完成:
首先将位串的所有位关闭。
fori = [0, n)
bit[i] = 0;
第二个阶段,读取文件中数据,对位串进行置位。
for each i in input file
bit[i]= 1;
第三个阶段则是根据位串的每位的值,相应地在文件中输出数字
fori = [0, n)
if bit[i] == 1
writei to the output file
补充:
1.不考虑内存的一种解法:
#include<stdio.h>
#include<stdlib.h>
#definen 10000000
intfcmp( const void * x, const void * y)
{
if( *((long*) x) > *((long*) y))
return1;
else if( *((long*) x) < *((long*) y))
return-1;
return0;
}
voidmain()
{
longinput = 0;
longdata[n];
longcounter = 0;
memset(data,0, n * sizeof(data));
FILEfp = open(“C://temp.txt”);
while(!eof(fp))
{
fscanf(fp, “%ld”, &input);
data[i]= input;
counter++;
}
qsort(data, counter, sizeof(long), fcmp);
for( int j = 0; j < counter; ++j)
{
fprintf(fp, “%ld ”,data[j])
}
}
2.使用位逻辑运算实现位向量:
//#define set(data, i) ((int)((int)data +i >> 5) | (1 << (i & 0x1F)))
#defineBITSPERWORD 32
#defineSHIFT 5
#defineMASK 0x1F
inta[1 + N / BITSPERWORD];
voidset(int i) { a[ i >> SHIFT] |= (1 << (i & MASK));}
voidclr(int i) {a[i >> SHIFT] &= ~(1 << (i & MASK));}
inttest(int i) {return a[ i >> SHIFT] & (1 << (i & MASK));}
3.写程序生成 0 ~ n-1 之间的随机顺序的整数。
将整个范围的数字放入数组,然后随机选择下标值,然后将当前的下标与随机选择的下标值进行交换。依次进行选择,最后可以得到一个范围内的K个随机值。
#include<stdio.h>
#include<stdlib.h>
#defineMAXN 2000000
intx[MAXN];
//根据给出的范围,随机选择其中的一个值。(选择下标值)
intrandint(inta, int b)
{
returna + (RAND_MAX * rand() + rand()) % (b + 1 - a)
}
int main( intargc, char * argv[])
{
int I, k, n, t, p;
srand((unsigned) time(NULL));
k = atoi(argv[1]);
n = atoi(argv[2]);
for( i = 0; i<k; i++)
{
x[i] = i;
}
for(i=0; i<k; i++)
{
p = randint(i, n-1);
t = x[p]; x[p] = x[i];x[i] = t;
printf(“%d\n”, x[i]);
}
}
5.如果内存值严格控制为1MB,那么对于10000000个整数要使用1.25MB内存该如何处理?
可以采取分次排序的方法,将所有的数据分为两部分,5000000一次,第一次先排序0-4909999,第二次再排序后面一半的数字。
整个算法的时间将增加,两次读取文件,读取时间加倍。
6.对于每个整数最多出现次数不超过10次的解决方法:
此时由于要记录数字对应的出现次数,因此一位无法完成,由于最多重复次数不超过10,因此我们可以用四位来表示一个数字,这样,原来需要一次完成,则需要四次才能完成。结合5题中的方法,将所有的数字分为四次来处理。这样可以实现算法。