几类面试题的思考与总结

1、从N个数里选最XX的K个

这个问题是问得最多的。一般情况下K会远小于N。这道题有两个思路,一是参照快速排序的思想,递归地划分,直至找到左边的K个。快排在最坏的情况下,是会退化为平方的,所以要加上随机化。即使不随机化,也有些比较复杂的算法保证复杂度不会很高,可以参照《算法导论》里求第K大的数的算法。如果答出这个算法,估计面试官都佩服你。另一个思路,则比较保险,维护一个堆,里面存K个数,考察下一个数的时候,先让它跟堆顶元素比较,如果合乎要求,再把堆顶元素出堆,然后把下一个元素放进去。如果面试官数学好的话,还会让你用概率分析一下这个算法的效率。假设原来要考虑第M(M>K)个数了,则这个数要放进堆里的概率为M个数的所有排列中,第M个数是前K大的数的排列所占比例,用排列公式算一下就行了。

下面来具体分析一下,上面两种方案的复杂度,数学不好的可以跳过。

对于维护堆的情况,每次调整是lgK的复杂度,N次就是NlgK。这是最坏的情况。下面作平均情况的分析。对一个大小为K的堆,当堆里元素不足K的时候,是不用堆的插入操作,等到元素等于K的时候,再作一个建堆操作,这个操作的复杂度是O(K)而不是O(KlgK)。而后在每次插入中,第M个元素要插入到堆中的概率是K/M(根据排列的知识算一下,化简后的结果),N次的结果,就是对K/M,K/(M+1),K/(M+2)……求和,这其实就是Harmonic数列,当然还要减开Hk的部分,这个数列是发散的,不能求和,但是可以比较精确地近似它的和,是lnN数量级的。所以最终的复杂度为O(n+lnN lnK),去掉低阶,化简为O(n)。

对于仿照快排的方式,最坏的情况下,当然是O(n^2)的。分析一下平均的情况。每次切分,都要遍历当前区间,但是平均情况下,区间的大小是指数减少的。所以每次要遍历的节点的求和,应该是等比数列求和,n+n/2+n/4+n/8+……。求和后应该是O(n)的。因为左端有K个数后,就不应再切分了,所以求和公式最小项并不到1,但是这并不影响复杂度。

现在分析一下K的影响,对于方案1来说,K越大,速度会越慢;而对于方案二来说,K越大,速度会越快。

 

2、最大连续和

这个问题问得太多了。应该一般人都知道是用贪心的算法可以线性求解了。如果问到二维的情况,目前二维的情况还没找到很好的解决方案。回答用贪心的方法得出O(n^3)的算法应该也可以了

3、求是否存在连续的一段的和等于指定的数(N)

这个问题可以认为是上一个问题的宽泛化。不过解法要复杂一些。把零元素到任何一个元素的连续和都求出来,然后排序。然后再枚举一个数,再用二分法查找它+N之后的数是否在数组里。这个转化比较巧妙,要慢慢体会。

4、交换两个数

如果直接说用C++的swap是不行的。一个可行的方案是引入一个中间变量。更好的方案是通过加减法,a+=b,b=a-b,a-=b。最佳的答案应该是通过位运算。X := X XOR Y;Y := X XOR Y;X := X XOR Y

5、已经每个客户的上线、下线时间,求每一时刻的在线人数,为了简化问题,跨越零点的情况视为零点前下前,然后再重新上线

客户的数量会有几千万(大家应该猜到是哪间公司的题目了),对每个客户应该在常数时间内完成统计。开一个c[24*60*60]的数组,表示一天里每一秒,如果用户在A时刻上线,B时刻下线,则c[A]++,C[B]--。完成对每一个客户的处理后。遍历一次C,求出从元素零到该元素的连续和,这就是该时刻的在线人数。

下面可以证明这个算法是最好的,因为它对每个客户是O(1)时间的,对每个时刻也是O(1)的,想更加效就只有抽样了。这个算法的转换也是很巧妙的。

6、已知一个数组里,超过一半的元素是同一个数,求这个数

用平方的算法肯定是不行的。排序的nlgn的算法也不够好。用HASH表之类很耗空间的算法也是不行。最简单的算法是作一次线性扫描,记录一下当前的数,及它出现的次数,如果下一个数跟它不同,则把它的次数减1,减到零,则从一个数开始重新计数。最终剩下的数就是要求的数了。

这题的关键是,那个数出现了一半以上。如果单纯地求出现次数最多的数就不能这样了。

7、从一个字符串里查找另一个字符串

一般人可能以为标准答案是KMP。如果考官继续问KMP是怎么操作的,那就难作答了。倒不是说KMP难理解,而是太巧妙了,很难说清楚。其实在实践中,KMP甚至比不上朴素的查找算法。因为朴素的算法,平均情况下,也是线性的,而常数要低些,可以假设每一位出现任一字母的概率是相同的,就可以求解了,当然这个假设太强了,即使概率分布有差异,结果也是一样的。

研究得更多的是,在一个长串里找一个短串,这样可以对短串作很多预处理,从而达到低而线性的复杂度,比如BM系统。

不过这个问题太偏了,应该不会被问到

你可能感兴趣的:(c,算法,面试)