问题描述
输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=107。如果在输入文件中有任何正数重复出现就是致命错误。没有其他数据与该正数相关联。
输出:按升序排列的输入正数的列表。
约束:最多有1MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要进一步优化。
程序设计与实现概要:
应用位图或位向量表示集合。可用一个10位长的字符串来表示一个所有元素都小于10的简单的非负整数集合,例如,可以用如下字符串表示集合{1,2,4,5,8}:
0 1 1 0 1 1 0 0 1 0
代表集合中数值的位都置为1,其他左所有的位置为0.编程珠玑当中建议是建一个具有1000万个位的字符串来表示这个文件,那么这个文件的所占容量为10000000 bit=107bit,不到1MB的大小,其中,当且仅当整数i在文件中存在,第i为1,这个表示利用了该问题的三个在排序问题中不常见的属性:输入数据限制在相对较小的范围内;数据没有重复;而且对于每条记录而言,除了单一个整数外没有其他关联数据。
如给定表示文件中整数集合的位图数据结构,则可以分三个阶段来编写程序
第一阶段:将所有的位都置为0,从而将集合初始化为空。
第二阶段:通过读入文件中的每个整数来建立集合,将每个对应的位置都置为1。
第三阶段:检验每一位,如果该位为1,就输出对应的整数,有此产生有序的输出文件。
下面的C语言的实现和C++的实现代码
所申请的int数组如下所示:
字节位置=数据/32;(采用位运算即右移5位)
位位置=数据%32;(采用位运算即跟0X1F进行与操作)。
C语言:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#include <stdio.h>
#define MAX 10000000
#define SHIFT 5
#define MASK 0x1F
#define DIGITS 32
int
a[1+MAX/DIGITS];
void
set(
int
n)
//将逻辑位置为n的二进制位置为1
{
a[n>>SHIFT] |=(1<<(n&MASK));
//n>>SHIFT右移5位相当于除以32求算字节位置,n&MASK相当于对32取余即求位位置,
}
void
clear(
int
n)
{
a[n>>SHIFT] &=(~(1<<(n&MASK)));
//将逻辑位置为n的二进制位置为0
}
int
test(
int
n)
{
return
a[n>>SHIFT] & (1<<(n&MASK));
//测试逻辑位置为n的二进制位是否为1
}
int
main(
int
argc,
char
*argv[])
{
int
i,n;
for
(i=1;i<=MAX;i++)
{
clear(i);
}
while
(
scanf
(
"%d"
,&n)!=EOF)
{
set(n);
}
for
(i=1;i<=MAX;i++)
{
if
(test(i))
printf
(
"%d "
,i);
}
return
0;
}
|
C++(使用bitset)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#include <iostream>
#include<bitset>
using
namespace
std;
int
main(
int
argc,
char
*argv[])
{
const
int
max = 10000000;
int
n,i;
bitset<max+1> bit;
//初始默认所有二进制位为0
while
(
scanf
(
"%d"
,&n)!=EOF)
{
bit.set(n,1);
//将第n位置1
}
for
(i=0;i<=max+1;i++)
{
if
(bit[i]==1)
printf
(
"%d "
,i);
}
return
0;
}
|
摘自:http://www.cnblogs.com/biyeymyhjob/archive/2012/08/14/2636933.html
对个别错别字进行了修正,排版略有变化。