阿里巴巴面试题目

1. 数轴上从左到右有n个点, a[0] a[1]…… 给定一根长度为l绳子,求绳子最多覆盖其中几个点。

 1 void maxCoverNum(int* a, int n, int l) {
 2    int maxCover = 1;
 3    int begin = 0, end = 1;
 4    while (endPos <= n – 1 ) {
 5        if (a[end] – a[begin] >= l) {        
 6           if (a[end] – a[begin] == l ) {
 7              maxCover = end–begin + 1 > maxCover ? end–begin + 1 : maxCover;  
 8           } else if (a[end] – a[begin] > l ) {
 9              maxCover = end–begin > maxCover ? end - begin : maxCover ;
10           } 
11           begin++; 
12        }
13        end++;
14    }
15    return maxCover;
16 }

 

2. 文件中有 10G 个整数,乱序排列,找出中位数,内存限制为 2G。写出思路即可(内存限制为2G的意思就是,可以使用2G的空间来运行程序,不考虑这台机器上的其他软件的占用内存)。

当样本数为奇数时,中位数=(N+1)/2 ; 当样本数为偶数时,中位数为N/2与1+N/2的均值。

 分析:和一般的查找中位数的题目有几点不同。

  > 数据不能读进内存,不然可用快速选择,如果数的范围合适还可以考虑桶排序或者计数排序,假设是32位整数,仍有4G种取值,需要16G的数组来计数。

  > 看成从N个数中找出第K大的数,如果K个数可读进内存,利用最小或最大堆,但这里K=N/2,有5G个数,仍不能读进内存。

  > 对于N个数和K个数都不能一次读进内存,《编程之美》里给出一个方案:设k<K,且k个数可以完全读进内存,那么先构建k个数的堆,先找出第0~k大的数,再扫描一遍数组找出第k+1~2k的数,再扫描直到找出第K个数。虽然每次时间大约是nlog(k),但需要扫描ceil(K/k)次,这里要扫描5次。

  > 基于字节的桶排序是一个可行的方法 

  将整形的每1byte作为一个key,一个int可拆成4个key,最高位的key越大,int越大。如果高位key相同,则比较次高位的key。
  1) 把10G整数每2G读入一次内存,然后一次遍历这536,870,912个数据。每个数据用位运算">>"取出最高8位(31-24)。这8bits最多表示255个桶,那么可以根据8bit的值来确定丢入第几个桶。把每个桶写入一个磁盘文件中,在内存中统计每个桶内数据的数量,只需要255个int即可。
  代价: (1) 10G数据依次读入内存的IO代价

      (2)在内存中遍历536,870,912个数据,O(n)的线性时间复杂度

      (3)把255个桶写会到255个磁盘文件空间中,这个代价是额外的,也就是多付出一倍的10G数据转移的时间。

  2) 根据内存中255个桶内的数量,计算中位数在第几个桶, 2,684,354,560个数中位数是第1,342,177,280个。if 前127个桶相加少于1,342,177,280,把第128个桶数据量加上,大于1,342,177,280。说明,中位数在磁盘的第128个桶中, 而且在这个桶的第1,342,177,280-sum(0-127)个数位上。(每个文件的大小估计在10G/128=80M左右,当然也不一定,但是超过2G的可能性很小)。
  代价:(1)循环计算255个桶中的数据量累加,需要O(M)的代价,其中m<255。(2)读入一个大概80M左右文件大小的IO代价。
  注意,需要读入的第128号文件仍然大于2G,那么整个读入仍然可以按照第一步分批来进行读取。

  3)继续以内存中的整数的次高8bit进行桶排序(23-16)。过程和第一步相同,也是255个桶。
  4)一直下去,直到最低字节(7-0bit)的桶排序结束。我相信这个时候完全可以在内存中使用一次快排就可以了。
 
  如果第二步过后,内存可以容纳下存在中位数的某一个文件的话,直接快排就可以了。

4. 1024! 末尾有多少个0?

  阶乘会产生0是因为数中有2和5两个因子,显然2因子的个数要大于5因子的个数,因此只需要统计5因子的个数。
  从1加到N每经过5个就可以有一个被5整除的数,所以1024能被5整除的数有1024/5=204
  那些能被25整除的数含有两个5的因子,但是上一步只有1个5因子已经计算过一次,所以1024/25=40
  1024/125=8
  1024/625=1
  共有1+8+40+204=253个

5.  用天平(只能比较,不能称重)从一堆小球中找出其中唯一一个较轻的,使用x次天平 最多可以从y个小球中找出较轻的那个,求y与x的关系式  

  每次均分成3份,y = 3^x,每次都能排除2/3

  有一个很大很大的输入流,大到没有存储器可以将其存储下来,而且只输入一次,如何从这个输入流中随机取得m个记录:蓄水池抽样。

6. 求一个数组的最长递减子序列比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}。不必连续

  类似编程之美中的求数组中最长递增子序列问题。可以用DP解决。

  假设在目标数组array[]的前i个元素中,最长递减子序列的长度为LDS[i]。那么

    LDS[i+1] = max{1,LDS[k]+1},其中array[i+1]<array[k],for any k<=i。

  if array[i+1]<array[k],那array[i+1]可以添加到LDS[k]长的子序列后构成长度为LDS[k]+1的递减子序列。array[i+1]本身构造成长度为1的子序列。

7.  题目:输入一个正数n,输出所有和为n连续正数序列。

  输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。

  1): 因为输出的整数序列是有序的,可设两个指针small,big,通过判区间[small,big]的和是否为N来得到这个序列。

    if sum(small,big) > n: small++

    if sum(small,big) < n: big++

    if sum(small,big) ==n : print 

    时间复杂度是0(n).

  2): 设 a + (a+1) + ... + b = n 是一个答案,那么 (a+b) * (b-a+1)/2 = n, 则 (a+b)和 (b-a+1)分别是2n的一个因子,枚举其中一个,就可以判断求出第二个,然后求出a和b了。时间复杂度是O(sqrt(n)),2n的因子范围肯定在[2,sqrt(2n)]之间。

      假设b-a+1是其中一个因子,可以通过2n % i == 0找到一个因子i, 然后根据(a+b)*(b-a+1)/2 = n的可推导出 2*a*i = 2n - i^2 + i, 因此只要判断a存在正整数解,就可以知道满足上面条件的2n的另一个因子也是存在的,进而可以求出a和b。

posted on 2015-06-08 15:05  keketse  阅读(258)  评论(0编辑  收藏  举报

导航