阅读《编程珠玑》遇到的问题1:给定包含4300 0000 000个32位整数的顺序文件,如何找出出现至少两次的整数?
该题来自《编程珠玑》第二章课后习题第2题,课后给出的答案如下所示,但是这个答案我没有看懂, 求解惑。
疑问:顺序文件可以随机访问吗?我想访问第10个整数,就必须要从得先访问第1个、第2个、、第9个吗?
如果支持随机访问的话,我们可以用二分法对这个顺序文件进行搜索,时间复杂度应该为log(n),空间复杂度为O(1), 我就只需把我们随机访问的数放在内存里就可以了。
如果不可以随机访问的话,那么我们就一次遍历这个顺序文件,检查相邻两个元素的是否相等,就可以找到找到一个至少出现两次的整数了。而且每次我们只需要将当前访问的数放在内存中,故空间复杂度应该为O(1),时间复杂度为O(n)。
但是从课后给出的答案来看,我的想法应该是有问题的。
后来发现,我读错题了。顺序文件并不是说这4300 000 000 000个整数按照从小到大的顺序排列的,
它只是说由一些系列的记录由某种顺序排列而成的文件,一般来说这些记录都是定长的。
解决方案:
二分搜索通过递归搜索包含半数以上整数的子区间来查找至少出现两次的单词。
搜索的区间: [left, right], 此时data数组中的所有数的范围都在[left, right]中,而数组中数的数量 > (right - left + 1)
middle = (left + right) / 2
搜索data,将它分成两个数组,一个数组中所有的数 <= middle, 一个数组中的所有数都 > middle
(注意,此处分成的数组并不是对半分的,可以一个数组里什么都没有,一个数组里包含了所有,再一次搜索的时候我们还要遍历相同规模的数据,复杂度将为nlog n)
选择包含半数以上的区间,接着如上方式搜索。
当left ==right时,此时此时data数组中的所有数的范围都在[left, right]中,即都为left, 而数组中数的数量 > 1, 故此我们找到了一个至少出现两次的整数。
初始条件:
改进:
上述方案的时间复杂度为 nlog n。主要是因为我们在将一个data数组根据middle划分为两个数组时,一个数组
中的数量可能过多导致的。
我们其实不需要将整个数组都遍历一遍,只要遍历到第(right - left + 2)个就可以了,这里面一定存在重复值。可以缩短我们每次的遍历时间,降低时间的复杂度。
我的理解是支持随机访问,但是得不到logn,因为无法迭代减半。是要找重复数,而不是某个数,所以作者才说不能保证每次减半,最差的情况类似于分治法,每次迭代的另一半都需要再迭代,复杂度nlogn。线性查找相邻的数是否重复的思路应该是正确的。
仅供参考:
//随机产生100000000个取值范围为[0~2的32次方减1]的数据,
//然后让用户输入一个数据,判断用户输入的数据是不是包含在前面随机产生的数据中。
//要求:当用户输入完成后,必须在1毫秒(千分之一秒)之内完成判断。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>
long int i;
unsigned long ul;
unsigned long *d;
unsigned long ulrand(void) {
return (
(((unsigned long)rand()<<24)&0xFF000000ul)
|(((unsigned long)rand()<<12)&0x00FFF000ul)
|(((unsigned long)rand() )&0x00000FFFul));
}
void main() {
d=(unsigned long *)calloc(1<<(32-5),sizeof(unsigned long));
if (NULL==d) {
printf("Can not calloc(%d,%d)!\n",1<<(32-5),sizeof(unsigned long));
return;
}
srand(time(NULL));
for (i=0;i<100000000;i++) {
while (1) {
ul=ulrand();
if (0==(d[ul>>5]&(1lu<<(ul%32)))) {
d[ul>>5]|=1<<(ul%32);
break;
}
}
if (0==i%1000000) printf("%09d,%10lu\n",i,ul);
}
while (1) {
printf("\nInput a number:");
fflush(stdout);
rewind(stdin);
if (1==scanf("%lu",&ul)) break;
}
if (d[ul>>5]&(1<<(ul%32))) printf("Include.\n" );
else printf("Not include.\n");
free(d);
}
//000000000,2135468533
//001000000,2465805973
//002000000,3844079964
//003000000,1883232874
//004000000,1204697784
//005000000,4050838287
//006000000,3802081245
//007000000,1586042671
//008000000,3119931368
//009000000, 251096899
//010000000,3491239701
//011000000,3365323844
//012000000,2191846708
//013000000,1879478195
//014000000,1112631457
//015000000,1927301519
//016000000,1717332861
//017000000,2922278240
//018000000, 694854106
//019000000, 273255526
//020000000, 398518467
//021000000,3270756812
//022000000,1500289424
//023000000,1502241936
//024000000,1770380660
//025000000,3668842116
//026000000,3255869879
//027000000,1299184024
//028000000,1072990028
//029000000, 242094712
//030000000,3789344297
//031000000,2599365925
//032000000, 962754138
//033000000,2055075654
//034000000,4083452879
//035000000, 489250842
//036000000, 611455230
//037000000, 277350616
//038000000,1597410795
//039000000,3224173662
//040000000,2291446877
//041000000,2546280575
//042000000,2509145642
//043000000,2371773252
//044000000, 635555963
//045000000,2674538666
//046000000,4253690312
//047000000,2675755514
//048000000,1269320296
//049000000,3172516920
//050000000,1430265210
//051000000, 196156173
//052000000,2470825669
//053000000,2536750977
//054000000,1182829949
//055000000,3202826434
//056000000,2263336265
//057000000, 313302924
//058000000,3630264578
//059000000,1154892716
//060000000,2985304230
//061000000,1252204837
//062000000,1292076720
//063000000, 242249250
//064000000,3999999961
//065000000, 431166416
//066000000,1366947236
//067000000,1414387330
//068000000,2143784481
//069000000,3242175409
//070000000,4158065163
//071000000,1425449573
//072000000,2493600232
//073000000,1316783455
//074000000,3723170478
//075000000,3064111466
//076000000, 408557403
//077000000,3722586955
//078000000,3801652651
//079000000,3788160154
//080000000,3329440047
//081000000,1408976868
//082000000, 471838899
//083000000,2145198260
//084000000,3781081738
//085000000,3439027738
//086000000,1150808750
//087000000,2782578638
//088000000, 85604584
//089000000,2704078162
//090000000, 584840269
//091000000,3854577719
//092000000,2823653537
//093000000, 797877025
//094000000,2248017755
//095000000,1787038685
//096000000,2816548567
//097000000, 489107494
//098000000, 911680090
//099000000,3677777147
//
//Input a number:3677777147
//Include.