题目来自网上,但是一些题目都没找到答案,《编程之美》还没看,这里写了下自己的思路,望指点。
【一】 时间受限
大部分的面试题,都是对时间复杂度有所要求的,如果有涉及,“最快”一类的字样,毫无疑问,先上时空原理,用空间来换时间。Hash,大数组,一些辅助性的空间,都是首选。在我的面试经历中,有无数次用到过Hash和大数组的。不过,通常这不会是面试官想听的唯一解法,他们紧接着十有八九是会说“如果只有xxxx空间呢?”。说此类方法只是为自己争取更多的时间,并且体现思考的完整性,简而言之,装B用。。。
eg1.1:求一个char(8bit)中,二进制1的个数,越快越好。 -- 《编程之美》
a:除了编程之美中的5种方法外,还可以计算汉明权重来统计,参见
http://en.wikipedia.org/wiki/Hamming_weight
eg1.2:有一个整数数组A[N],让你不用除法,求另一个数组B[N],其中B[i] = A[0]*A[1] ... * A[N-1] / A[i],期望复杂度是O(N)。 -- TopLanguage
a:等大牛解答
【二】 空间受限
这里的空间受限,指的是在大数据分析的逻辑下,空间受限的问题。大部分情况下,就是压缩。位图是一个很好的方法,用一个bit(或几个)取代更大的int类型,最常见的位图是1bit 取代 1int,其实,很多时候,1bit可以取代更大的空间,这完全取决于你需要保留的信息。。。
eg2.1:有一个很大的文件,存放一堆7位的电话号码,号码无重复,请用最小的内存消耗,将其排序。 -- 《编程珠玑》
a:每个号码对应位图的一位,位图初始全清零,读入一个号码就把相应的位置位,遍历后按位图顺序输出对应的数字。
假设7位电话共有9999999个,按1000万记,位图占用内存约为1.25MB.
eg2.2:给10MB的内存,给一个4百万整数的文件,找一个不在文件中的整数。 -- (传说中的Google面试题)
a:同上
【三】 基于文件
越来越多的大公司,开始关系对文件的处理,上面所说的空间受限的问题,其实也基本都是和文件打交道。基于文件的处理,基本都是寻找,或者排序,最最核心的,就是减少文件读取的次数。除了位图法,还可以考虑哨兵,典型的案例就是外排中,增加单个文件大小的方法。
eg3.1:给定一个包含4300000000个32位整数的顺序文件,找到一个至少出现两次的整数。 -- (传说中的微软面试题,我曾经遇到过类似的)
a:方法1,可以也用位图的方式,但在置位前先判断一下是否已经置位,如果是,就是重复出现了。
方法2,编程珠玑中的方法是用二分法把这些数分为A,B两类,因为32位整数最多表示429xxxxxxx,所以元素较多的那类必然存在重复。依次类推。
eg3.2:有一个文件,有很多很多的整数(也许有100亿),寻找其中最大的K个。 -- 《编程之美》
a:同上一题的方法2,但是效率低
【四】 常见方法
你需要相信,面试官也是人,他不会有心情花30分钟给你描述一个问题,或者让你做50页纸的推导,考算法的目的只是为了你的思维能力,而不是真的想让你搞定一个复杂的问题。大部分问题,都是有比较快速清晰的解决方法的。。。
1. 分治法 这绝对是你必须考虑使用的一种方法,如果有可能的话。动态规划这东西,在面试的时候比较沉重,不好描述,不好书写,而分治却刚刚好,美丽,快捷,易书写,是面试官杀人越货的首选武器。分治的用法实在是太多了,几乎是无所不在,二分,快排,种群计数,各个唯美无比。。。
eg4.1:给你一个长度为N的整数数组,请找出最大的子数组和。 -- 《编程之美》
a:可以用分治法,但是《编程珠玑》给了一个O(n)的方法更快。
maxsofar = 0;
maxendinghere = 0;
for i = [1, n)
maxendinghere = max(maxendinghere + x[i], 0);
maxsofar = max(maxendinghere, maxsofar);
eg4.2:求一个int(32bit)中,二进制1的个数。 -- 《代码之美》(注,不是编程之美是另一本书,有一章叫做种群计数,这是一种很酷的分治)
a:除了编程之美中的5种方法外,还可以计算汉明权重来统计,参见
http://en.wikipedia.org/wiki/Hamming_weight
2. 排序和查找 排序出现的次数实在是太多了,很重要的一点,排序的东西才能用二分。二分是如此好用,以至于我们总是想着排序。查找和排序总是紧密联系的,当然,仅仅是为了查找,做一次排序,你需要衡量一下代价。。。
eg4.3:有一个论坛,有ID发帖数目超过总数的一半,给你论坛所有帖子的ID列表,请你找到这个水王。 - -《编程之美》
a:方法1:排序,然后去中间的那个ID,必然是总数超过一半的ID。
方法2:不排序,还没想到。
eg4.4:给一组一维的空间 [1, 6] [2, 4] ... ,请求是否有区间重叠。 -- 《编程之美》
a:对空间[x, y]的x进行排序,然后比较相邻2个[x1, y1], [x2, y2]中y1 是否大于> x2,如果是,那就是重叠。
3. 减小问题规模 很多时候,题目看上去很吓人,仔细分析一下,就可以刨去其中大部分的无关内容,获得真正的出题意图,这一点很重要。另外有些时候,题目会在空间上做出一些限制,这个时候,你可以考虑动态的对数据规模进行缩减,比如用减法或除法抵消,用抑或抵消,等等。。。
eg4.5:给一个整数N,求它的阶乘N!,有几个0结尾。 -- 《编程之美》
a:没想到
eg4.6:盒子里有三种颜色的球,红黄蓝,可以用任意两个不同颜色的球,换两个另外颜色的球,比如1红 + 1黄 = 2蓝。现在盒子里面有171个红球,172个黄球,173个蓝球,问,能不能经过若干次交换,最终变成同一颜色的球。 -- TopLanguage
a:不能,最多只能是某种颜色0个,另一种1个,其余是第三种颜色。
eg4.8:有一组数,除了一个数只有1个,其他都是两两成对的,请找出那一个不成对的数。另,如果不成对的数有两个,该如何是好。 -- TopLanguage
a:题目没懂
4. 常量法 典型的速餐方法,它的思想是,一组数,在某些情况下,和一定,通过这个常量,进行反推,可快速搞定一些问题。。。
eg4.9:有一副扑克牌(你可以用任意方式来表示),被抽去一张,请快速找出这抽去的一张是什么? -- 微软面试题,活生生
a:提示已经说明了,算一下目前牌的数值总和x,原来完整的总和是y,则丢掉的牌是y-x。
5. 编码 编码真是个好东西,它可以将复杂的问题抽象化。比如,对一个序列进行编码,可以直接映射到数组脚标上,大大提高访问速度。。。
eg4.10:最近一次百度笔试题
a:?
eg4.11:有1000瓶超级名贵的葡萄酒,其中有1瓶有毒。这种毒药很厉害,哪怕被稀释了1000000倍还是可以毒死人的。但这个毒药一定时间后才会毒发,时长是1个月。为了不浪费这些葡萄酒,有1000个壮士决定花5周的时间将毒酒找出,他们只希望最多有10个人牺牲,你需要如何安排才能实现。 -- TopLanguage
a:题意没懂,不能直接叫每个人把每瓶酒都尝一下?有一个人死了,那瓶酒就是有毒的。
6. 概率
eg4.12:有一个长度为N的链表,N未知。希望你只遍历一次链表,就从链表中等概率的挑出K个数。 -- TopLanguage
a:首先挑出前k个数,保存在pick[1...k]中,然后从第k+1个开始遍历
for i = k+1 to N do //这里N不知道,但是可以用链表->next == null 来判断是否到达链表末尾。
r = random(1, i);
if (1 <= r <= k);
pick[r] = i;
简单数学证明如下:
归纳法,算法刚开始,对于前k个数被选中的概率都为1,,不失一般性,选择其中的第j个来讨论,
i = k+1轮:
random(1, i)返回值为j的概率为1/k+1,所以j保留下来的概率为k/k+1
i = k+2轮:
random(1, i)返回值为j的概率为1/k+2,所以j保留下来的概率为(k/k+1) * (k+1/k+2) = k/k+2
...
i = N轮
random(1, i)返回值为j的概率为1/N,所以j保留下来的概率为(k/k+1) * (k+1/k+2)*....* (N-1/N) = k/N
对于第k+1到第N个数,选择其中的数m来讨论,
当i = m时:
random(1, i)返回值在[1, k]内的概率为k/m,所以j保留下来的概率为k/m,设m保存在第s位
i = m+1轮:
random(1, i)返回值为s的概率为1/(m+1),所以j保留下来的概率为(k/m) * (m/m+1) = k/(m+1)
...
i = N轮
random(1, i)返回值为s的概率为1/N,所以j保留下来的概率为(8/m) * (m/m+1) *....* (N-1/N) = k/N
得证。
【五】 加速方法
很多时候,你给的算法基本正确,但是还不够优秀。面试官会希望你优化一下。优化的方法有很多,就基本的思路就是,考虑一下到底哪里出现了浪费。常见的浪费有两种,一种是用了比较沉重的运算,比如除法、取模等,你可能需要为计算来加速。另外有时候,你的算法还太粗线条,比如只需要符号,你却计算了总数等等。。。
eg5.1:求两个数的最大公约数。 -- 《编程之美》
a:还没做
eg5.2:有一个整数数组A[N],求其中连续N-1个数的最大乘积。 -- 《编程之美》
a:应该和上面连续和是一个道理
eg5.3:估计一下快速排序的比较次数。 -- 《代码之美》
a:没做
【六】 数据结构
大部分面试时候,我们都是面向数组来设计算法,因为简单变化多,面试官好把握。但其他数据结果,同样也很重要。AVL,B树那样的可能比较复杂,但是链表、树这样的结构,也经常出没,我个人就碰见多次。。。
1. 链表
eg6.1:给你一个单链表的头指针,在不使用大量附加数据或修改原有数据的前提下,检查一个单链表是否有环。 -- 微软面试题,活生生
a:使用快慢两个指针,慢指针p = p->next,快指针q = q->next->next,如果相遇,那么就有环。
eg6.2:给你两个链表,如何判断其是否相交,如果相交,如何找到两个链表的第一个交点。 -- 《编程之美》
a1:把其中一个头尾相连变成环,然后判断另一个链表是否有环。
a2:2个链表都遍历到尾部,即p->next==null && q->next==null, 然后判断p == q。
eg6.3:只给你一个指向链表中某元素的指针,请删除该元素。 -- 《编程之美》
a:将后一个元素复制到当前元素p->value = p->next->value,然后删除后一个元素。
2. 树
eg6.4:写堆排序的算法
a:堆排序基本方法,见我之前的常见堆介绍。
eg6.5:判断一棵二叉树T中,是否包含另一颗二叉树P的结构。 -- 微软面试题,活生生
a:题意没懂?二叉树P的结构?