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

本文探讨了在大量整数文件中查找至少出现两次的整数的方法,介绍了三种算法:利用位图优化内存使用的一次性扫描算法,通过二分搜索范围优化查找效率的算法,以及基于排序的线性查找算法。每种方法都针对内存限制和搜索效率进行了优化。

《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;  
            }  
        }  
    }  
}  



<think>嗯,用户想了解索引顺序文件的折半查找索引和组内顺序查找需要多少次查找。我得先回忆一下索引顺序文件的结构。索引顺序文件通常分为索引区和数据区,数据区分成若干组,每组对应一个索引项。索引是按关键字排序的,所以查找时先用折半查找找到对应的索引项,然后在组内进行顺序查找。 首先,折半查找的时间复杂度是O(log₂n),这里n是索引项的数目。假设索引区有n个块,每个块对应一个数据组。那折半查找的次数大概是log₂n次。不过具体次数可能需要向上取整,因为次数必须是整数。 接下来是组内的顺序查找。每个数据组有m个记录,顺序查找的平均查找次数是(m+1)/2。不过如果组内元素也是有序的,是否可以用折半查找呢?但题目里明确说是组内顺序查找,所以这里应该用顺序查找的平均次数。 所以总查找次数应该是折半查找的次数加上顺序查找的次数。例如,索引区有n个块,每组有m个记录,总次数就是log₂n + (m+1)/2。不过需要注意这里的log₂n是否需要向上取整,实际应用中可能需要考虑这一点,因为不能进行小数次查找。 另外,可能需要举一个例子来具体说明。比如索引区有100个块,每组有10个记录。折半查找次数大约是log₂100≈7次,顺序查找平均(10+1)/2=5.5次,总次数大约是12.5次。这样用户更容易理解。 不过有没有特殊情况需要考虑?比如索引项和数据组的对应方式,是否每个索引项对应一个数据组,还是多个?还有如果数据组内的记录数不平均怎么办?题目里可能假设每组的大小相同,所以可以按平均情况处理。 另外,是否需要区分成功查找和失败查找的情况?题目可能只关心成功的情况,所以平均查找次数是针对成功的情况。 最后,总结的时候要明确总次数由两部分组成,并给出公式和例子,这样用户就能清晰理解了。</think>索引顺序文件采用折半查找索引 + 组内顺序查找的检索过程,其查找次数可通过以下步骤分析: 1. **文件结构特征** - 数据记录被分为若干组,每组对应一个索引项 - 索引表按关键字有序排列(支持折半查找) - 每组内部记录无序(仅支持顺序查找) 2. **查找次数公式** 总查找次数 = 折半查找索引次数 + 组内顺序查找次数 $$ ASL = \lceil \log_2(N+1) \rceil + \frac{M+1}{2} $$ (其中N为索引项数,M为组内平均记录数) 3. **具体计算示例** 假设索引表有100项(N=100),每组含20条记录(M=20): - 折半查找次数:$$\lceil \log_2(100+1) \rceil = 7$$ - 组内查找次数:$$\frac{20+1}{2} = 10.5$$ - **总平均查找次数**:7 + 10.5 = 17.5次 4. **效率对比** - 相比纯顺序查找(平均(100×20+1)/2≈1000次),效率提升显著 - 相比完全折半查找(需全文件有序,平均$$\lceil \log_2(2000) \rceil = 11$$次),空间利用率更优 5. **优化方向** - 保持索引项有序性的同时,可尝试对大数据组建立二级索引 - 合理设置组大小,使$$\log_2N \approx \frac{M}{2}$$时总效率最优 这种结构在数据库索引、文件系统目录组织等场景中广泛应用,平衡了存储空间与检索效率。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值