zoukankan      html  css  js  c++  java
  • 给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 完

  • 相关阅读:
    Ubuntu常用命令
    Linux 虚拟机安装Ubuntu
    word 2019 方框中打勾
    apache httpd的安装和虚拟主机配置(基于centos 7)
    LoadRunner 12.5 community edition 试玩+Linux主机监控
    Directory Service目录服务
    TCP/IP脑图
    Qt样式选择器
    常见电脑进入BIOS的快捷键
    网线(RJ45接口)的接法
  • 原文地址:https://www.cnblogs.com/venow/p/2870431.html
Copyright © 2011-2022 走看看