位排序

一.问题描述

输入:一个最多包含n个正整数的文件,每个数都小于n,且数据不重复,其中n = 10000000。

输出:按升序排列输出正数列表。

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

二.程序设计与实现概要

(1)如给定表示文件中整数集合的位图数据结构,则可以分三个阶段来编写程序

第一阶段:将所有的位都置为0,从而将集合初始化为空。

第二阶段:通过读入文件中的每个整数来建立集合,将每个对应的位置都置为1。

第三阶段:检验每一位,如果该为为1,就输出对应的整数,有此产生有序的输出文件。

 

(2)整个程序的思想就是:

1.每个整数有32位,那么它就可以表示32个数,分别对应每bit位为1.
2.然后把10000000个数分为1+N/BITSPERWORD组(相当于有这么多个桶),每组包含接近32个数。

(3)所申请的int数组如下所示:


(4)下面的C语言的实现和C++的实现代码

#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#define N 10000000			// 最大数
#define BITSPERWORD 32		<span style="white-space:pre">	</span>// 宽度
#define SHIFT 5
#define MASK 0x1F

// 字节位置 = 数据/32;(采用位运算即右移5位)
// 位位置 = 数据%32;(采用位运算即跟0X1F进行与操作)


// 将逻辑位置为i的二进制位置为1
void set(int arr[], int i)
{
    arr[i>>SHIFT] |= (1<<(i&MASK));		// arr[i/32] = arr[i/32] | (1<<(i%32))
}

// 测试逻辑位置为i的二进制位是否为1
int test(int arr[], int i)
{
    return arr[i>>SHIFT] & (1<<(i&MASK));	// arr[i/32] = arr[i/32] & (1<<(i%32))
}

// 将逻辑位置的二进制位置为0
void clear(int arr[],int num)
{
    memset(arr, 0x00, num*4);
}

int main()
{
	int *arr;       // 用于存储元素是否存在

    // 栈的最大空间一般为2M(与系统有关),因此要堆中分配
	arr = (int *)malloc(sizeof(int)*(N/BITSPERWORD+1));
	clear(arr,N/BITSPERWORD + 1);

	// 先在文本文件in.txt中生成N个数

    FILE* in_file = freopen("in.txt", "r", stdin);
    FILE* out_file = freopen("out.txt", "w", stdout);

    if(in_file != NULL)
    {
        int d;
        while(scanf("%d", &d) != EOF)
            set(arr, d);
        fclose(in_file);
    }

    if(out_file != NULL)
    {
        for(int i = 0; i < N; i++)
            if(test(arr, i))
                printf("%d\n", i);
    }
    fclose(out_file);

    return 0;
}

三.拓展

给40亿个不重复的unsigned int的整数,没有排过序,然后再给一个数,如果快速判断这个数是否在那40亿个数当中。(腾讯面试题)


用位图法:40亿unsigned int,则用位图表示的话需要大小为40亿个bit=4*109 bit=0.5*109 bytes 因此申请的内存只需要大小约为512MB左右,这样在内存每个bit代表一个unsignedint整数,并将每个bit初始化为0,然后将40亿个unsigned int的整数读入,每个unsigned int的整数对应bit设置为1,读入后,最后看所给定的数对应的bit是否为1,是1存在,否则不存在。







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值