面试时算法题的解答思路

面试中纯粹考算法的问题一般是让很多程序员朋友痛恨的,这里分享下我对于解答算法题的一些思路和技巧。

一般关于算法的文章,都是从经典算法讲起,一种一种算法介绍,见得算法多了,自然就有了感悟,但如此学习花费的时间和精力却是过于巨大,也不适合在博客里面交流。这一篇文,却是专门讲快捷思路的,很多人面对算法题的时候几乎是脑子里一片空白,这一篇文章讲的就是从题目下手,把毫无思路的题目打开一个缺口的几种常见技巧。

(一)由简至繁

事实上,很多问题确实是很难在第一时间内得到正确的思路的,这时候可以尝试一种由简至繁的思路。首先把问题规模缩小到非常容易解答的地步。

[题目]有足够量的2分、5分、1分硬币,请问凑齐1元钱有多少种方法?

此题乍看上去,只会觉得完全无法入手,但是按照由简至繁的思路,我们可以先考虑极端简单的情况,假如把问题规模缩小成:有足够量的1分硬币,请问凑齐1分钱有多少种方法?毫无疑问,答案是1。

得到这一答案之后,我们可以略微扩大问题的规模: 有足够量的1分硬币,凑齐2分钱有多少种方法?凑齐n分钱有多少种方法?答案仍然是1

接下来,我们可以从另一个角度来扩大问题,有足够量的1分硬币和2分硬币,凑齐n分钱有多少种方法?这时我们手里已经有了有足够量的1分硬币,凑齐任意多钱都只有1种方法,那么只用1分钱凑齐n-2分钱,有1种方法,只用1分钱凑齐n-4分钱,有1种方法,只用1分钱凑齐n-6分钱,有1种方法......

而凑齐这些n-2、n-4、n-6这些钱数,各自补上2分钱,会产生一种新的凑齐n分钱的方法,这些方法的总数+1,就是用1分硬币和2分硬币,凑齐n分钱的方法数了。

在面试时,立刻采用这种思路是一种非常有益的尝试,解决小规模问题可以让你更加熟悉问题,并且慢慢发现问题的特性,最重要的是给你的面试官正面的信号——立即动手分析问题比皱眉冥思苦想看起来好得多。

对于此题而言,我们可以很快发现问题的规模有两个维度:用a1-ak种硬币和凑齐n分钱,所以我们可以记做P(k,n)。当我们发现递归公式 P(k,n) = P(k-1,n - ak) + P(k-1,n - 2*ak) + P(k-1,n - 3*ak) ... ... 时,这个问题已经是迎刃而解了

通常由简至繁的思路,用来解决动态规划问题是非常有效的,当积累了一定量简单问题的解的时候,往往通向更高一层问题的答案已经摆在眼前了。

 

(二)一分为二

另一种思路,就是把问题一刀斩下,把问题分为两半,变成两个与原来问题同构的问题,能把问题一分为2,就能再一分为4,就能再一分为8,直到分成我们容易解决的问题。当尝试这种思路时,其实只需要考虑两个问题:1.一分为二以后,问题是否被简化了? 2.根据一分为二的两个问题的解,能否方便地得出整个问题的解?

[题目]将一个数组排序。

这个经典算法肯定所有人都熟悉的不能再熟悉了,不过若是从头开始思考这个问题,倒也不是所有人都能想出几种经典的排序算法之一的,这里仅仅是用来做例子说明一分为二的思路的应用。

最简单的一分为二,就是将数组分成两半,分别排序。对于两个有序数组,我们有办法将它合并成一个有序数组,所以这个一分为二的思路是可行的,同样对于已经分成两半的数组,我们还可以将这个数组分作两半,直到我们分好的数组仅有1个元素,1个元素的数组天然就是有序的。不难看出,按这种思路我们得出的是经典数组排序算法中的“归并排序”。

还有另一种一分为二的思路,考虑到自然将数组分成两半合并起来比较复杂,我们可以考虑将数组按照大于和小于某个元素分成两半,这样只要分别解决就可以直接连接成一个有序数组了,同样这个问题也是能够再次一分为二。按照这个思路,则可以得出经典数组排序算法中的“快速排序”。

 

(三)化虚为实

这种思路针对的是浮点数有关的特殊问题,因为无论是穷举还是二分,对于浮点数相关的计算问题(尤其是计算几何)都难以启效,所以化虚为实,指的是把有点"虚"的浮点数,用整数来替代。具体做法是,把题目中给出的一些浮点数(不限于浮点数,我们不关心其具体大小的整数也可以)排序,然后用浮点数的序号代替本身来思考问题,等到具体计算时再替换回来。

[题目]已知n个边水平竖直的矩形(用四元组[x1,y1,x2,y2]表示),求它们的总共覆盖面积。

因为坐标可能出现浮点数,所以此题看起来十分繁复(可以实践上面由简至繁和一分为二的思路都基本无效),略一思考,矩形的覆盖关系其实只跟矩形坐标的大小有关,所以我们尝试思考将矩形的所有x值排序,然后用序号代替具体竖直,y值亦然,于是我们得到所有矩形其实处于一个2nx2n的区块当中,这样我们用最简单的穷举办法,可以计算出每一个1x1的格子是否被覆盖住了。至此,只要我们计算面积的时候,把格子的真实长宽换算回来,就已经得到题目的答案了。

 

本文是某天在QQ群里讨论面试时的算法问题时想到要写的,以上三种思路,是我平时遇到算法问题的快速思考方向,并非万灵药方,若是不能生效,就要静下心来慢慢思考观察了,考虑到面试的时候基本不会遇到高难度算法题,这几种技巧的命中率应该不会太低,共享给大家,希望有所帮助。

posted @ 2011-03-01 03:44 winter-cn 阅读(4532) 评论(34) 编辑 收藏

 回复 引用 查看   
#1楼 2011-03-01 06:51 gbb21      
very nice !
 回复 引用 查看   
#2楼 2011-03-01 07:53 huyong      
写得不错。佩服。
 回复 引用 查看   
#3楼 2011-03-01 09:10 真爱无悔      
多谢分享。
 回复 引用 查看   
#4楼 2011-03-01 10:09 小乐2011      
对于此题而言,我们可以很快发现问题的规模有两个维度:用a1-ak种硬币和凑齐n分钱,所以我们可以记做P(k,n)。当我们发现递归公式 P(k,n) = P(k-1,n - ak) + P(k-1,n - 2*ak) + P(k-1,n - 3*ak) ... ...
没看明白....

 回复 引用 查看   
#5楼 2011-03-01 10:29 wgz      
引用小乐2011:
对于此题而言,我们可以很快发现问题的规模有两个维度:用a1-ak种硬币和凑齐n分钱,所以我们可以记做P(k,n)。当我们发现递归公式 P(k,n) = P(k-1,n - ak) + P(k-1,n - 2*ak) + P(k-1,n - 3*ak) ... ...
没看明白....

握手,楼主就此题能再说说吗?

 回复 引用 查看   
#6楼 2011-03-01 11:19 一Yuyi一      
多谢分享。


 回复 引用 查看   
#7楼 2011-03-01 11:20 小乐2011      
嗯 是啊 楼主 期待详解...
 回复 引用 查看   
#8楼 2011-03-01 11:23 blackcore      
要入心、再入手!
 回复 引用 查看   
#9楼[楼主] 2011-03-01 12:03 winter-cn      
引用小乐2011:
对于此题而言,我们可以很快发现问题的规模有两个维度:用a1-ak种硬币和凑齐n分钱,所以我们可以记做P(k,n)。当我们发现递归公式 P(k,n) = P(k-1,n - ak) + P(k-1,n - 2*ak) + P(k-1,n - 3*ak) ... ...
没看明白....

有足够量的1分硬币和2分硬币,凑齐n分钱有多少种方法?这时我们手里已经有了有足够量的1分硬币,凑齐任意多钱都只有1种方法,那么只用1分钱凑齐n-2分钱,有1种方法,只用1分钱凑齐n-4分钱,有1种方法,只用1分钱凑齐n-6分钱,有1种方法......

对照这一段就可以了


 回复 引用 查看   
#10楼 2011-03-01 12:20 卡通一下      
引用winter-cn:
引用小乐2011:
对于此题而言,我们可以很快发现问题的规模有两个维度:用a1-ak种硬币和凑齐n分钱,所以我们可以记做P(k,n)。当我们发现递归公式 P(k,n) = P(k-1,n - ak) + P(k-1,n - 2*ak) + P(k-1,n - 3*ak) ... ...
没看明白....

有足够量的1分硬币和2分硬币,凑齐n分钱有多少种方法?这时我们手里已经有了有足够量的1分硬币,凑齐任意多钱都只有1种方法,那么只用1分钱凑齐n-2分钱,有1种方法,只用1分钱凑齐n-4分钱,有1种方法,只用1分钱凑齐n-6分钱,有1种方法...

记得上小学时,邻居给我出了一道题,说是用一架天平,来称1-32克的东西,最少需要几只法码?

当时只会冥思苦想,用了足足一个小时,哈哈...

 回复 引用 查看   
#11楼[楼主] 2011-03-01 12:32 winter-cn      
@卡通一下
呵呵 这个题懂了二进制就很容易了 若是小时候做确实难得离谱

 回复 引用 查看   
#12楼 2011-03-01 12:41 卡通一下      
引用winter-cn:
@卡通一下
呵呵 这个题懂了二进制就很容易了 若是小时候做确实难得离谱

哦,你来说说是几只多少克的法码,呵呵!

 回复 引用 查看   
#13楼 2011-03-01 12:54 Ivony...      
引用winter-cn:
@卡通一下
呵呵 这个题懂了二进制就很容易了 若是小时候做确实难得离谱


天平两边都能放砝码的,所以还不是二进制。。。。

 回复 引用 查看   
#14楼 2011-03-01 12:57 Ivony...      
比如说我有两个砝码,1克和3克的,那么:

称1克,货品左边,一克砝码右边。
称2克,货品和一克砝码左边,三克砝码右边。
称3克,货品左边,三克砝码右边
称4克,货品左边,三克和一克砝码右边。

1克和3克的就可以称1-4克的货品了。

 回复 引用 查看   
#15楼 2011-03-01 13:00 卡通一下      
引用Ivony...:
引用winter-cn:
@卡通一下
呵呵 这个题懂了二进制就很容易了 若是小时候做确实难得离谱


天平两边都能放砝码的,所以还不是二进制。。。。

是这样,所以也请你猜一猜,呵呵!

 回复 引用 查看   
#16楼[楼主] 2011-03-01 13:11 winter-cn      
@卡通一下
呵呵 开始想简单了 如果考虑到可以把砝码放在两边 好像就有点复杂了
我就来按第一种思路由简至繁做做
称1克 需要1个砝码 1
称1-2克 需要2个砝码 1 1或者1 2或者1 3
称1-3克 也只需要2个砝码 1 2 或者1 3
称1-4克 也只需要2个砝码 1 3
称1-12克 需要3个砝码 1 3 9
称1-40克 需要4个砝码 1 3 9 27

所以答案是4个砝码?1 3 9 27

 回复 引用 查看   
#17楼 2011-03-01 13:20 Ivony...      
引用卡通一下:
引用Ivony...:
引用winter-cn:
@卡通一下
呵呵 这个题懂了二进制就很容易了 若是小时候做确实难得离谱


天平两边都能放砝码的,所以还不是二进制。。。。

是这样,所以也请你猜一猜,呵呵!



这个不用猜,有规律可循也肯定有公式可以算的。

最多只需要四个砝码,1、3、9、27。

1: 货       = 1
2: 货+1     = 3
3: 货       = 3
4: 货       = 3+1
5: 货+3+1   = 9
6: 货+3     = 9
7: 货+3     = 9+1
8: 货+1     = 9
9: 货       = 9
10:货       = 9+1
11:货+1     = 9+3
12:货       = 9+3
13:货       = 9+3+1
14:货+9+3+1 = 27
15:货+9+3   = 27
16:货+9+3   = 27+1
17:货+9+1   = 27
18:货+9     = 27
19:货+9     = 27+1
20:货+9+1   = 27+3
21:货+9     = 27+3
22:货+9     = 27+3+1
23:货+3+1   = 27
24:货+3     = 27
25:货+3     = 27+1
26:货+1     = 27
27:货       = 27
28:货       = 27+1
29:货+1     = 27+3
30:货       = 27+3
31:货       = 27+3+1
32:货+3+1   = 27+9

 回复 引用 查看   
#18楼 2011-03-01 13:27 Ivony...      
我只能证明4个砝码是必要条件,但没有找到充分条件的证明方法。

必要条件非常容易证明:

每个砝码只可能有三种状态,放在货物一边,放在另一边,不放上去。

所以所有砝码在天平和天平外的摆放可能只有3^n种(n是砝码个数)。

所以要组合出32种情况,则必须n≥4。

 回复 引用 查看   
#19楼 2011-03-01 13:30 卡通一下      
@Ivony...
@winter-cn

哇,我自己都忘记了,哈哈...

不过这肯定有一种算法,只不过是我们没有接触过的领域。

 回复 引用 查看   
#20楼[楼主] 2011-03-01 13:42 winter-cn      
@卡通一下
@Ivony...
可以证明的至少需要4个砝码
假设3个砝码可以
那么每个砝码有 不放 放待称量物同侧 放待称量物另一侧三种状态
3个砝码共有27种状态 不可能表示1-32共32个值

另外结合两个子结论可以证明3个砝码最多能表示1-13
1.任何砝码组合能称量0
2.任何砝码组合如果能称量x 则也能称量-x

 回复 引用 查看   
#21楼[楼主] 2011-03-01 13:49 winter-cn      
@Ivony...
充分条件用数学归纳法证明
1 3 9 ... 3^n可称量 1到(3^(n+1)-1)/2
=>1 3 9 ... 3^(n+1)可称量 1到(13^(n+2)-1)/2

这个很容易证明了(13^(n+1)-1)/2分成3段 分别构造出来就可以了

 回复 引用 查看   
#22楼 2011-03-01 13:50 诺贝尔      
mark
 回复 引用 查看   
#23楼 2011-03-01 13:50 Ivony...      
终于找到证明方式:

因为每个砝码可以有三种状态,放在货物一边,放在另一边,不放上去。

令有n个砝码重量分别是G1、G2...Gn。

那么G1放在货物一边,G2放在另一边,其余不放,可以称得的货物的重量是:
G2-G1

变换一下,等于:
-1*G1 + 1*G2 + 0*G3 + ... + 0*Gn

由此可以看出,如果我们令第n个砝码放在货物一边取Cn=-1,放在另一边取Cn=1,不放上去取Cn=0,则可以称得的货物重量即是:

货物重量 = C1*G1 + C2*G2 + ... + Cn*Gn。

现在的问题是,在Cn ∈ { -1, 0, 1 }的前提下,问我们要哪些Gn才能使得C1*G1 + C2*G2 + ... + Cn*Gn可以得出{1,2,...,32}的结果

那我们知道,如果Cn ∈ { 0, 1, 2 }的时候,Gn取3^(n-1)是最好的结果。只需要4个砝码:3^0、3^1、3^2、3^3即可以得到0-80的结果。

所以对等式做一些变换:


货物重量 = C1*G1 + C2*G2 + ... + Cn*Gn + G1 + G2 + ... + Gn - G1 - G2 - ... - Gn
将+G1、+G2拆到多项式的每一项:
货物重量 = G1+C1*G1 + G2+C2*G2 + ... + Gn+Cn*Gn - G1 - G2 - ... - Gn

货物重量 = (1+C1)*G1 + (1+C2)*G2 + ... + (1+Cn)*Gn - G1 - G2 - ... - Gn


所以,最多只需要四个砝码:1、3、9、27,可以称得最多:80 - 1 - 3 - 9 - 27 = 40克的货物

 回复 引用 查看   
#24楼 2011-03-01 13:58 卡通一下      
@Ivony...
@winter-cn

我细细地想了一下,用 1、3、8、25 四只法码就可以了,它们最高可以称到37克,而且似乎这是“最小的组合”,呵呵!

 回复 引用 查看   
#25楼[楼主] 2011-03-01 14:21 winter-cn      
@卡通一下
1、3、9、27这种是最佳组合 剩下的也有很多办法 1、3、8、20刚好可以称足32克

 回复 引用 查看   
#26楼 2011-03-01 14:50 卡通一下      
引用winter-cn:
@卡通一下
1、3、9、27这种是最佳组合 剩下的也有很多办法 1、3、8、20刚好可以称足32克

你说得很对,这是因为我给了先决条件,呵呵!

这类问题暂且叫“配重问题”吧,它的规律是,下一个法码的重量,等于已有法码的重量之合,再乘2加1 。这可能是最优的组合,但后面的数学原理我就说不出来了,哈哈...

 回复 引用 查看   
#27楼 2011-03-01 15:00 Ivony...      
引用卡通一下:
引用winter-cn:
@卡通一下
1、3、9、27这种是最佳组合 剩下的也有很多办法 1、3、8、20刚好可以称足32克

你说得很对,这是因为我给了先决条件,呵呵!

这类问题暂且叫“配重问题”吧,它的规律是,下一个法码的重量,等于已有法码的重量之合,再乘2加1 。这可能是最优的组合,但后面的数学原理我就说不出来了,哈哈...


嗯,还有一个问题,即:
称量{0,1,...,M}克的货物,最少需要的砝码数n必须满足:
3^n - 1 - 3^(n-1) - 3^(n-2) ... 3^0 ≥ M

也即:
(3^n - 1) / 2 ≥ M
是必要条件。

上面已经证明了这个是充分条件,只要再证明是必要条件,即证明了这是最优解之一。

顺手证明下:
3^n - 1 - 3^(n-1) - 3^(n-2) ... 3^0 = (3^n - 1) / 2

令An = 3^(n-1) + 3^(n-2) +...+ 3^0
我们已知:
a^n = (a-1) * a^(n-1) + (a-1) * a^(n-2) +...+ (a-1) * a^0 + 1(套用等比数列求和公式可得。)

所以:
3^n = 2 * 3^(n-1) + 2 * 3^(n-2) +...+ 2 * 3^0 + 1
    = 2An + 1

所以:
An = (3^n - 1) / 2

所以:
3^n - 1 - 3^(n-1) - 3^(n-2) ... 3^0 = 3^n - 1 - An = 3^n - 1 - (3^n - 1) / 2 = (3^n - 1) / 2

 回复 引用 查看   
#28楼 2011-03-01 16:22 卡通一下      
@Ivony...

老兄,你的数学比我要好多了,呵呵!

 回复 引用 查看   
#29楼 2011-03-01 20:16 curer      
天平是最完美的数学模型之一,不仅仅是是表面上用来衡量平衡。

在上面的最优解中 ,在所组成的所有情况中。每一个元素的使用次数都是一致的。比如,4时 ,1,3都是3次。13时 1,3,9 都是9次。
我们只有把每一个元素都公平的对待,都不浪费,我们才能获得我们的最优解。

而其他解法,比如1、3、8、20,在构造的这个范围内,者4个元素使用的频率是不平等的。而这也就构不成最优解。
而这为什么最优解是1,3,9,都是3的次幂呢? 因为对我们每个元素来说,有3种情况,左,右,没有。
...经过一段神奇的证明
...我们得出每个元素都平等。
呵呵,完美的数学。

 回复 引用 查看   
#30楼 2011-03-01 20:21 curer      
看了这篇文章,我感觉我的大脑吹过一阵清风。呵呵,难得一见的文章。
 回复 引用 查看   
#31楼 2011-03-01 21:49 木+头      
好东西要慢慢品
 回复 引用 查看   
#32楼 2011-03-02 07:36 卡通一下      
引用curer:
天平是最完美的数学模型之一,不仅仅是是表面上用来衡量平衡。

在上面的最优解中 ,在所组成的所有情况中。每一个元素的使用次数都是一致的。比如,4时 ,1,3都是3次。13时 1,3,9 都是9次。
我们只有把每一个元素都公平的对待,都不浪费,我们才能获得我们的最优解。

而其他解法,比如1、3、8、20,在构造的这个范围内,者4个元素使用的频率是不平等的。而这也就构不成最优解。
而这为什么最优解是1,3,9,都是3的次幂呢? 因为对我们每个元素来说,有3种情况,左,右,没有。
...经过一段神奇的证明
...我们得出每个元素都平等。
呵呵,完美的数学。

我的数学水平很低,只是过去常听人家说数学之美,但从没有真正地体会过,也不清楚什么是数学之美,呵呵!

现在看了你的帖子,对你的论点感觉很是神奇,也许数学之美就是融于神奇之中吧!

 回复 引用 查看   
#33楼 2011-03-02 09:39 Ivony...      
23楼证明了,对于(3^n - 1) / 2 ≥ M,则一定有一个解。这个解就是Gn = 3^(n-1)。

接下来只要证明对于对于(3^n - 1) / 2 < M,则一定没有解,那么23楼得到的解便一定是最优解之一了。

首先考虑所有砝码重量均不相等,且货物不为零重量的情况,我们知道天平称重的原理是两边平衡后根据砝码的重量推算货物的重量,简单说不平衡的天平是无法称重的。所以我们可以简单得出,放在货物一边的砝码重量和不能大于另一边的,否则天平一定不能平衡。

若有砝码集合S,则令f(S)等于S中所有砝码的总重量

现在来考虑一种摆放:令货物一边的砝码集合为A,另一边的砝码集合为B,若此时天平平衡,我们知道(A)一定不能大于f(B),即f(A) ≤ f(B)

又因为货物重量不为零,所以f(A) ≠ f(B),即f(A) < f(B)。

所以很明显的可以得到,若货物一边的砝码集合为B,另一边的砝码集合为A,则天平就不可能平衡了。

故对于任何一种合理的摆放<A,B>,则<B,A>必然是不合理的。

n个砝码一共有3^n种摆放方式,其中都不放上去由于只能称量无重量货物,所以不合理,还剩下3^n-1种,刚才我们证明了,这些摆放方式中,如果有一种摆放方式是合理的,那么就必然有一个反向摆放方式是不合理的,换言之合理的摆放方式最多只能有(3^n-1)/2种。

这就证明了砝码重量不相等,货物重量不为零时,(3^n - 1) / 2 ≥ M必然是一个最优解。

 回复 引用 查看   
#34楼 2011-03-02 10:55 卡通一下      
@Ivony...

看了两篇你的数学推导帖子,觉得你一定是个非常喜爱数学的人,呵呵!