给10^7个无重复的整数排序

题目:给10^7个无重复的整数排序,即1到10000000,尽量使空间复杂度小。

答:首先,我们利用程序来随机生成这10^7个整数,用一个全局数组存储着10^7个整数,然后根据随机生成的下标交换其中两个整数的位置,以达到我们所说的整数是随机的,具体请看下面的代码:

const int N = 10000000;
int data[N] = {0};

void swap(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}
void generate_no_repeat_number(void)
{
    FILE *fp = fopen("unsort_data.txt", "w");
    assert(fp != NULL);
    for (int i = 0; i < N; i++)  //先按整数顺序初始化
    {
        data[i] = i + 1;
    }
    int p ,q;
    srand((unsigned)time(NULL));
    for (int i = 0; i < N; i++)  //通过随机交换下标的方法,来使者10^7个整数达到真正的随机
    {
        p = (rand() * RAND_MAX + rand()) % N;
        q = (rand() * RAND_MAX + rand()) % N;
        swap(data[p], data[q]);
    }
    for (int i = 0; i < N; i++)
    {
        fprintf(fp, "%d ", data[i]);
    }
    fclose(fp);
}

  对于无重复的整数排序,用位图法是最合适不过了,下面我们就来看看如何用位图来实现对这10^7个整数排序。

  第一种方法,我们可以借助一个数组,比如数组的第一个元素就表示0-31,第二个元素就表示32-63。这样一个整数就用一个bit来表示。请看下面的代码:

int flag[N / 32 + 1] = {0}; //函数堆栈默认1M,此数组大小大约为1.2M,已然大于默认堆栈大小,故用全局数组表示
void sort_by_array(void)
{
    clock_t begin = clock();
    FILE *fpread = NULL;
    FILE *fpwrite = NULL;
    fpread = fopen("unsort_data.txt", "r");
    fpwrite = fopen("sort_by_array.txt", "w");
    assert(fpread != NULL);
    assert(fpwrite != NULL);
    int num;
    while (EOF != fscanf(fpread, "%d", &num)) //从未排序的文件中读入数据
    {
        flag[num / 32] |= (1 << (num % 32));  //将num对应数组中的某个元素的1位置为1
    }
    for (int i = 1; i <= N; i++)
    {
        if ((flag[i/32] >> (i % 32)) & 1)  //从数组开始对每一个元素的每一位测试,若为1则输出该数
        {
            fprintf(fpwrite, "%d ", i);
        }
    }
    clock_t end = clock();
    cout<<"借助数组排序所需时间为: "<<(end - begin)/CLK_TCK << "s"<<endl;
    fclose(fpread);
    fclose(fpwrite);
}

  第二种方法,直接借助STL中的bitset,这样就更加简单了,代码如下:

bitset<N + 1> bit_map;
void sort_by_bitset(void)
{
    clock_t begin = clock();
    FILE *fpread = NULL;
    FILE *fpwrite = NULL;
    fpread = fopen("unsort_data.txt", "r");
    fpwrite = fopen("sort_by_bitset.txt", "w");
    assert(fpread != NULL);
    assert(fpwrite != NULL);
    int num;
    bit_map.reset();  //全部清零
    while (EOF != fscanf(fpread, "%d", &num))
    {
        bit_map.set(num, 1);  //将该数对应的位 置为1
    }
    for (int i = 1; i <= N; i++)
    {
        if (bit_map[i] ==  1)  //若该数对应的位等于1,则输出该数
        {
            fprintf(fpwrite, "%d ", i);
        }
    }
    clock_t end = clock();
    cout<<"借助bitset排序所需时间为: "<<(end - begin)/CLK_TCK << "s"<<endl;
    fclose(fpread);
    fclose(fpwrite);
}

  我们首先来看,未排序的数字如unsort_data.txt所示:

  通过上面两种方法,对文件排序如下图所示,其中sort_by_array.txt借助于数组,sort_by_bitset.txt借助于bitset。

  上面的这两种方法,需要的内存大约都为1.2M,如果我只有1M不到的内存,那又该如何对这10^7个整数排序呢?其实不难,可以采用分治法,比如:先对1到5000000的整数排序,再对5000000到10000000的整数排序,请看下面代码,我只给出用数组去实现排序,内存使用大约为0.6M,如果空间复杂度要求更小,那可以接着对这些整数进行分治。STL的bitset就不写了,very easy。。。

const int MAX_PART = 5000000;
void sort_by_part_array(void)
{
    clock_t begin = clock();
    FILE *fpread = NULL;
    FILE *fpwrite = NULL;
    fpread = fopen("unsort_data.txt", "r");
    fpwrite = fopen("sort_by_part_array.txt", "w");
    assert(fpread != NULL);
    assert(fpwrite != NULL);
    int flag_part[MAX_PART / 32 + 1] = {0};
    int num;
    while (EOF != fscanf(fpread, "%d", &num))
    {
        if (num <= MAX_PART)
        {
            flag_part[num / 32] |= (1 << (num % 32));
        }
    }
    for (int i = 1; i <= MAX_PART; i++)
    {
        if ((flag_part[i/32] >> (i % 32)) & 1)
        {
            fprintf(fpwrite, "%d ", i);
        }
    }
    fseek(fpread, 0, SEEK_SET);
    memset(flag_part, 0, MAX_PART/32 + 1);
    while (EOF != fscanf(fpread, "%d", &num))
    {
        if (num > MAX_PART)
        {
            num -= MAX_PART;
            flag_part[num / 32] |= (1 << (num % 32));
        }
    }
    for (int i = 1; i <= MAX_PART; i++)
    {
        if ((flag_part[i/32] >> (i % 32)) & 1)
        {
            fprintf(fpwrite, "%d ", i + MAX_PART);
        }
    }
    clock_t end = clock();
    cout<<"借助部分数组排序所需时间为: "<<(end - begin)/CLK_TCK << "s"<<endl;
    fclose(fpread);
    fclose(fpwrite);
}

   下图为sort_by_part_array.txt与sort_by_array.txt的文件对比

  扩展:

  腾讯笔试题:判断数字是否出现在40亿个数中?给40亿个不重复的unsigned int的整数,没排过序的,然后再给几个数,如何快速判断这几个数是否在那40亿个数当中?

  如前所述用位图法,unsigned int 的取值范围是0到2^32-1。我们可以申请连续的2^32/8=512M的内存,用每一个bit对应一个unsigned int数字。首先将512M内存都初始化为0,然后每处理一个数字就将其对应的bit设置为1。当需要查询时,直接找到对应bit,看其值是0还是1即可。

  2013年1月22日 venow 完

posted @ 2013-01-22 20:19  venow  阅读(2623)  评论(0编辑  收藏  举报