算法——bit处理数据

题目

给定一个输入文件,包含40亿的非负整数,请设计一种算法,产生一个不在该文件中的整数。假设你有1GB内存来完成这项任务。如果只有10MB内存又该怎么办。假定所有值都是唯一的。[摘自程序员面试金典]
解题思路:一般遇到这种大批量的数据需要处理时,我们首先要告诉自己,解决方法肯定不会超出我们平时所学的。我们也可以适当的将题目简化。看着这么大的数据,我们不妨先从简单的做起。正常的,我们查找非负整数中不出现的数,在内存足够的情况下,我们可以定义一个数组,将每个数作为数组下标,出现了,改下标对应的值就加1,否则就为0.然后再来一次遍历就可以解决。这个想法能不能放在该题上呢?
40亿等于2^32次方。有1G的内存,既然所有值都是唯一的,我们完全可以用一个bit位来表示该数是否存在。1G的内存表示的bit位是80亿,明显是满足需要的。这里面我们也可以用BitSet类来解题。

    long numofInts=((long)Integer.MAX_VALUE+1);
    byte[] bitfield=new byte[(int)(numofInts/8)];
    void findNum() throws FileNotFoundException
    {
        Scanner in=new Scanner(new FileReader("file.txt"));
        while(in.hasNextInt())
        {
            int n=in.nextInt(); 
            /*
             * 使用OR操作符设置一个字节的第n位,
             * 找出bitfield中相对应的数字
             */
            **bitfield[n/8]|=1<<(n%8);**
        }
        for(int i=0;i<bitfield.length;i++)
        {
            for(int j=0;j<8;j++)
            {
                if((**bitfield[i]&(1<<j))==0**)
                {
                    System.out.println(i*8+j);
                    return;
                }
            }
        }

    }

如果只有10MB内存又该怎么办?

内存更小了,很容易的就会想到,分段处理。但是怎样个分段处理呢?
假如我现在有100个数,每次只能处理10个数,那怎么在100种找出没有出现的数呢。既然每次只能处理10个数,我将100分为10个区段。
第一个区段的数分别是1到10。我们将所有的数进行遍历,用一个count对这个区段出现的数进行累计,怎么累计呢?如果(i/10==1),那count++;
同理第二区段、第三区段。
那我们就可以用一个数组来表示所有的区段了,并对每个区段出现的数进行统计blocks[i/count]++;
这样我们通过记录,就可以找出缺数的区段。然后对这个区段运用上面的解法,记录然后遍历,就可以求解了。

    int  bitSize=2^20;
    int blockNum=2^12;
    byte[] bitfield=new byte[bitSize/8];
    int[] blocks=new int[blockNum];

    void findNums() throws FileNotFoundException
    {
        int starting=-1;
        Scanner in=new Scanner(new FileReader("file.txt"));
        while(in.hasNextInt())
        {
            int n=in.nextInt();
            blocks[n/(bitfield.length*8)]++;
        }
        for(int i=0;i<blocks.length;i++)
        {
            //如果blocks的值小于该区段的总数,则该区段中的数至少少1
            if(blocks[i]<bitfield.length*8)
            {
                starting=i*bitfield.length*8;
                break;
            }
        }
        in=new Scanner(new FileReader("file.txt"));
        while(in.hasNextInt())
        {
            /*
             * 若该该数字落在缺少数字的区块了,则记下该数字
             */
            int n=in.nextInt();
            if(n>=starting&&n<starting+bitfield.length*8)
            {
                bitfield[(n-starting)/8]|=1<<((n-starting)%8);
            }
        }
        for(int i=0;i<bitfield.length;i++)
        {
            for(int j=0;j<8;j++)
            {
                /*
                 * 取回各个字节的各个比特,当发现有比特为0时,找到相应的值
                 */
                if((bitfield[i]&(1<<j))==0)
                {
                    System.out.println(i*8+j+starting);
                    return;
                }
            }
        }
    }

内容摘自:程序员面试金典

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值