编程珠玑 第一章解析

问题:

输入:一个最多包含n个正整数的文件,每个数都小于n(n=1000W)。如果在输入文件中有任何整数重复出现就是致命错误。没有其他数据与该整数相关联。

输出:按升序排列的输入整数的列表。

约束:最多有(大约)1 MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要进一步优化了。

方法一:归并排序。(耗时间)

        归并排序需把数据全部读入内存,1000w个整数的大小是1000w*4/(1024*1024)大约是40M(假设一个整数是4字节),显然不能将全部数据读入内存。

方法二:分段排序。(耗时间)

       将这些整数分成40组,分别是[0-249999]  [250000-4999999]  … [9750000-9999999],然后遍历40次。这样时间复杂度是40n,空间复杂度是n/40,比归并要好点。当然也可以把范围扩大或者缩小。

方法三:位图法。

       用1位来表示[0~n-1]中的整数是否存在。1表示存在,0表示不存在。这样的话进行一次遍历,就可以进行排序了,而且空间复杂度是n/(8*1024*1024)。例如对于20个数来说,如果集合是{1,2,3,5,8,13}.那么其位图表示法如下:01110100100001000000(说明:左边第一位表示 0 )。

程序:

利用bitset

#include
#include
#include
using namespace std;
int main()
{
    // 查看bitset的实现可知bitset是利用对unsigned long进行位运算间接实现对位的存储的
    // 如是保存一个32位的bitset实质是保存了4个unsigned long长整数
    bitset<1000000> b;
    b.reset();   
    ifstream in("before_sort.txt");
    ofstream out("after_sort.txt");
    int i;
    while (!in.eof()) {
     in>>i;
     b.set(i);
    }
    for (i=0;i<1000000;++i) {
     if (b[i]==1) {
      out<      }  
    }
    return 0;
}

利用位逻辑运算

事实上,我们是用每一个元素表示一个32位的二进制字符串这样这个元素可以保留相邻32个号码是否存在的信息,数组范围就下降到了10000000/32了,例如对于号码89256,由于89256 mod 32=2789…8,这样我们应该             置a[2789]32位字符串的第8(从低位数起)1现在问题的关键是,如何用位逻辑运算来表示这种操作。

#include
#include

#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/32];

// 置位函数----用“|”操作符,I & MASK相当于mod操作

//m mod n 运算,当n = 2 的X次幂的时候,m mod n = m & (n-1)
void set(int i) {        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)); }
int main()
{       int i = 0;
        while (scanf("%d", &i))
   set(i);
        for (i = 0; i < N; i++)
                if (test(i))
     printf("%d\n", i);
        return 0;
}

你可能感兴趣的:(C/C++,算法)