位图排序

输入:一个最多包含一百万(10^6)个正整数的文件,每个数都小于n,其中n=一千万(10^7)。输入文件中没有重复的整数。

输出: 按升序排列这些数,并写入磁盘。

约束:有 1MB多(不超过2MB) 的内存空间可用,有充足的硬盘空间

一、原理

         这道题是在编程珠玑上看到的,是习题3,自己写了一下玩玩,感觉这个算法很棒,通俗易懂。

 这个问题的最好算法应当是位图:由于整数没有重复,那么可以用比特位来表示整数的存在。

比如集合  {1,2,5,4,6},

可以用位图表示为   01101110,

由于位图的第1,2,4,5,6为都是1,表示集合里存在着这几个数。

因为这个算法实现的时候,数最大为10 000 000,那么需要用10 000 000个bit的数组来表示,内存大小为 10 000 000/8/1024/1024约等于1.2M

实现需要解决以下问题有:

1) 找到数据i所对应的字节位置

2)找到数据i对应的字节中位位置

3) 判断某位是否为1, 置某位为1 

解决它:

1) 找到 对应字节位置: 32系统,相当于 i/32, 使用位操作 数据i >> 5

2)  找到对应字节的位位置: 32系统相当于 i%32, 使用位操作 数据 i&0x1F(31)

   附:(m mod n 运算,当n = 2的X次幂的时候,m mod n = m&(n-1)  ,这里32为2^5,所以是i&0X1F)

 n=2^x 写成二进制就是1后面x个0,m%n就是m的低x位。 
n-1=2^x-1 写成二进制是x个1,m&(n-1)也是取m的低x位。

3)数字 a 的 第i位 是1: 方法 a & (1 << i) ,将数据a的第 i位 置1: a | (1 << i)

二、代码

(因为C++STL中有bitset这一容器,所以实现起来并不难,我先用bitset实现,后用C++不用bitset实现)

/**************************************
*                                     *
*  -*- coding: utf-8 -*-              *
*  @Date    : 2016-05-12 18:54:47     *
*  @Author  : Zeroinger               *
*                                     *
 *************************************/
#include <iostream>
#include <algorithm>
#include <time.h>
#include <bitset>
#include <iostream>
#include <cstring>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <bitset>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <fstream>
using namespace std;
#define MAXN 10000000
#define NUM  1000000
#define SHIFT 5
#define MARK 0x1F
#define DIGITS 32
int a[MAXN+1];
//bitset <MAXN+1> bit;
int b[1+MAXN/32];
void st(int i)
{
    b[i >> SHIFT] |= (1 << (i & MARK));
}

int test(int i)
{
    return b[i >> SHIFT] & (1 << (i & MARK));
}

void clr(int i)
{
    b[i >> SHIFT] &= ~(1 << (i & MARK));
}
void write()
{

    for(int i=0; i<=MAXN; i++)
    {
        a[i]=i+1;
    }
}
int getrand(int a,int b)
{
    return  ((a+RAND_MAX * rand() + rand()) % (b-a+1));
}
void getdata()
{
    for(int i=0; i<NUM; i++)
    {
            int t=getrand(i,MAXN);
            swap(a[i],a[t]);
    }
    FILE *fp;
    fp = fopen("test_before.txt", "w");
    if(fp == NULL)
    {
        cout << "随机数文件生成失败,error in make_data()." << endl;
    }
    int cc=0;
    for(int i = 0; i < NUM; i++)
    {
        fprintf(fp, "%7d  ", a[i]);
        cc++;
        if(cc%20==0)
        fprintf(fp, "
");

    }
    cout<<cc<<endl;
    fclose(fp);
    cout << "随机数文件生成成功." << endl;
}
int main()
{
   // bit.reset();
   for(int i=0;i<MAXN;i++)
   {
       clr(i);
   }
    write();
    getdata();
    FILE *fpsrc;
    fpsrc = fopen("test_before.txt", "r");
    if(fpsrc == NULL)
    {
        cout << "打开文件失败" << endl;
        return 0;
    }

    int data;
    while(fscanf(fpsrc, "%d", &data) != EOF)
    {

           // bit.set(data,1);
           st(data);

    }
    FILE *fpdst;
    fpdst = fopen("test_after.txt", "w");
    if(fpdst == NULL)
    {
        cout << "打开文件失败" << endl;
        return 0;
    }
    int c=0;
    for(int i = 0; i <MAXN+1; i++)
    {
        if(test(i)/*bit[i] == 1*/)
        {
            fprintf(fpdst, "%7d ", i);
            c++;
            if(c%20==0)
              fprintf(fpdst, "
");

        }
    }
    cout<<c<<endl;
    return 0;
}

  

原文地址:https://www.cnblogs.com/Zeroinger/p/5495410.html