整数顺序文件查找至少出现两次的整数

《Programming Pearls》

2.2 给定包含4300000000个32位整数的顺序文件,如何找出一个出现至少两次的整数?


【方法一】

思路:考虑两个条件

1.所有的整数都存储在顺序文件中,因此,读取文件的次数将明显影响算法的效率;

2.顺序文件中包含的整数个数为4300000000,如果全部读取放在内存中的话,必须要考虑内存空间因素。

解决方案:

由上面的问题,想到了Bit-Map,可以申请537500000个char型数组,数组中每个位对应4300000000个整数中的一个数,刚开始时,将所有的位都置为0,如果有存在相对应的数,那么对应的位就置为一。

问题又出来了,如何才能表示至少包含两次的整数呢?

这时,我们发现,要表示至少包含两次的整数,仅用一位来表示是不够的。那么用两位呢?00表示没有数据,01表示存在一个,10表示存在两个,11表示存在两个以上。

这样,需要申请大小为1075000000的char类型的数组,两位对应一个数。

初始时,所有位都置0,然后开始读取顺序文件,读到整数后,相应的位做相应的改变。

这样,我们便只需要一次操作,而且使用了最少的内存便解决这个问题了。


【方法二】

BinarySearch

不是对文件内容折半,而是对搜索范围折半。由于4.3G>32位的整数空间,根据鸽笼原理一定会有重复的整数。搜索范围从所有的32位正整数开始(全部当成unsigned int,简化问题),即[0,2^32),中间值即为2^31。然后遍历文件,如果小于2^31的整数个数大于2^31,则调整搜索范围为[0,2^31],反之亦然;然后再对整个文件遍历一遍,直到得到最后的结果。这样一共会有logn次的搜索,每次过n个整数(每次都是完全遍历),总体的复杂度为O(nlogn)。

例子:数组[4,2,5,1,3,6,3,7,0,7],假定从3位的整数空间内搜索。第一次的范围为[0,8),遍历后发现[0,4)范围内的个数为5,于是调整为搜索[0,4)范围内的整数。第二次发现[2,4)范围内的整数为3,大于2,于是调整为[2,4)。再经过第三次的遍历,找出3为重复出现的整数。

改进:上面的办法有很多的冗余。于是提出了一个办法:建立一个新的文件(是顺序文件就可以)。在一次遍历过后确定搜索的范围后,把原有文件里这个范围内的整数写到新的文件里去,下次搜索就只要搜索这个文件了。这样可以近似线性的复杂度(但是常数项应该很大)。


【方法三】

若整数是排好序的,一个线性查找的解法

/** 
 * 问题描述: 
 * 给定包含4 300 000 000个32位整数的顺序文件, 
 * 如何找出一个至少出现两次的整数 
 *  
 * @author loo
 * 
 */  
public class FindTwice {  
      
    /** 
     * 由于4 300 000 000 >2^32,所以必然存在重复的整数 
     * 考虑到内存的问题,可以先读取一部分,然后查找 
     * 这里假设一次读取10个 
     */  
    public static void main(String[] args) {  
        int[] arr = {2,3,4,5,7,11,12,12,13,14,15};  
        int iCount=0;  
        int increase=arr[0];  // suppose=arr[0];
        for(;iCount<arr.length;iCount++){  // suppose++;
            if(arr[iCount]>iCount+increase){  // if(arr[i]>suppose)
                increase+=(arr[iCount]-iCount-increase);  // suppose+=arr[i];
                continue;  
            }  
            if(arr[iCount]<iCount+increase){ // if(arr[i]<suppose) 
                System.out.println("重复的数字是:"+arr[iCount]);  
                break;  
            }  
        }  
    }  
}  



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值