为什么我反对纯算法面试题(转载)

转自:http://coolshell.cn/articles/8138.html

算法面试可能是微软搞出来的面试方法,现在很多公司都在效仿,而且我们的程序员也乐于解算法题,我个人以为,这是应试教育的毒瘤!我在《再谈“我是怎么招程序员”》中比较保守地说过,“问难的算法题并没有错,错的很多面试官只是在肤浅甚至错误地理解着面试算法题的目的。”,今天,我想加强一下这个观点——我反对纯算法题面试!(注意,我说的是纯算法题)

我再次引用我以前的一个观点——

能解算法题并不意味着这个人就有能力就能在工作中解决问题,你可以想想,小学奥数题可能比这些题更难,但并不意味着那些奥数能手就能解决实际问题。

好了,让我们来看一个示例(这个示例是昨天在微博上的一个讨论),这个题是——“找出无序数组中第2大的数”,几乎所有的人都用了O(n)的算法,我相信对于我们这些应试教育出来的人来说,不用排序用O(n)算法是很正常的事,连我都不由自主地认为O(n)算法是这个题的标准答案。我们太习惯于标准答案了,这是我国教育最悲哀的地方。(广义的洗脑就是让你的意识依赖于某个标准答案,然后通过给你标准答案让你不会思考而控制你)

功能性需求分析

试想,如果我们在实际工作中得到这样一个题 我们会怎么做?我一定会分析这个需求,因为我害怕需求未来会改变,今天你叫我找一个第2大的数,明天你找我找一个第4大的数,后天叫我找一个第100大的 数,我不搞死了。需求变化是很正常的事。分析完这个需求后,我会很自然地去写找第K大数的算法——难度一下子就增大了。

很多人会以为找第K大的需求是一种“过早扩展”的思路,不是这样的,我相信我们在实际编码中写过太多这样的程序了,你一定不会设计出这样的函数接口 —— Find2ndMaxNum(int* array, int len),就好像你不会设计出 DestroyBaghdad(); 这样的接口,而是设计一个DestoryCity( City& ); 的接口,而把Baghdad当成参数传进去!所以,你应该是声明一个叫FindKthMaxNum(int* array, int len, int kth),把2当成参数传进去。这是最基本的编程方法,用数学的话来说,叫代数!最简单的需求分析方法就是把需求翻译成函数名,然后看看是这个接口不是很二?!

(注:不要纠结于FindMaxNum()或FindMinNum(),因为这两个函数名的业务意义很清楚了,不像Find2ndMaxNum()那么二)

非功能性需求分析

性能之类的东西从来都是非功能性需求,对于算法题,我们太喜欢研究算法题的空间和时间复杂度了。我们希望做到空间和时间双丰收,这是算法学术界的风格。所以,习惯于标准答案的我们已经失去思考的能力,只会机械地思考算法之内的性能,而忽略了算法之外的性能

如果题目是——“从无序数组中找到第K个最大的数”,那么,我们一定会去思考用O(n)的线性算法找出第K个数。事实上,也有线性算法——STL中 可以用nth_element求得类似的第n大的数,其利用快速排序的思想,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大 于等于X,Sb中元素小于X。这时有两种情况:1)Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;2) Sa中元素的个数大于等于k,则返回Sa中的第k大数。时间复杂度近似为O(n)。

搞学术的nuts们到了这一步一定会欢呼胜利!但是他们哪里能想得到性能的需求分析也是来源自业务的!

我们一说性能,基本上是个人都会问,请求量有多大?如果我们的FindKthMaxNum()的请求量是m次,那么你的这个每次都要O(n)复杂度的算法得到的效果就是O(n*m),这一点,是书呆子式的学院派人永远想不到的。因为应试教育让我们不会从实际思考了。

工程式的解法

根据上面的需求分析,有软件工程经验的人的解法通常会这样:

1)把数组排序,从大到小。

2)于是你要第k大的数,就直接访问 array[k]。

排序只需要一次,O(n*log(n)),然后,接下来的m次对FindKthMaxNum()的调用全是O(1)的,整体复杂度反而成了线性的。

其实,上述的还不是工程式的最好的解法,因为,在业务中,那数组中的数据可能会是会变化的,所以,如果是用数组排序的话,有数据的改动会让我重新排序,这个太耗性能了,如果实际情况中会有很多的插入或删除操作,那么可以考虑使用B+树。

工程式的解法有以下特点:

1)很方便扩展,因为数据排好序了,你还可以方便地支持各种需求,如从第k1大到k2大的数据(那些学院派写出来的代码在拿到这个需求时又开始挠头苦想了)

2)规整的数据会简化整体的算法复杂度,从而整体性能会更好。(公欲善其事,必先利其器)

3)代码变得清晰,易懂,易维护!(学院派的和STL一样的近似O(n)复杂度的算法没人敢动)

争论

你可能会和我有以下争论,

  • 如果程序员做这个算法题用排序的方式,他一定不会像你想那么多。是的,你说得对。但是我想说,很多时候,我们直觉地思考,恰恰是正确的路。因为“排序”这个思路符合人类大脑处理问题的方式,而使用学院派的方式是反大脑直觉的。反大脑直觉的,通常意味着晦涩难懂,维护成本上升。
  • 就是一道面试题,我就是想测试一下你的算法技能,这也扯太多了。没问题,不过,我们要清楚我们是在招什么人?是一个只会写算法的人,还是一个会做软件的人?这个只有你自己最清楚。
  • 这个算法题太容易诱导到学院派的思路了。是的这道“找出第K大的数”,其实可以变换为更为业务一点的题目——“我要和别的商户竞价,我想排在所有竞争对手报价的第K名,请写一个程序,我输入K,和一个商品名,系统告诉我应该订多少价?(商家的所有商品的报价在一数组中)”——业务分析,整体性能,算法,数据结构,增加需求让应聘者重构,这一个问题就全考了。
  • 你是不是在说算法不重要,不用学?千万别这样理解我,搞得好像如果面试不面,我就可以不学。算法很重要,算法题能锻炼我们的思维,而且也有很多实际用处。我这篇文章不是让大家不要去学算法,这是完全错误的,我是让大家带着业务问题去使用算法。问你业务问题,一样会问到算法题上来。
时间: 2025-01-20 02:23:03

为什么我反对纯算法面试题(转载)的相关文章

数据结构与算法面试题80道

由于这些题,实在太火了.所以,应广大网友建议要求,在此把之前已整理公布的前80题, 现在,一次性分享出来.此也算是前80题第一次集体亮相. 此些题,已有上万人,看到或见识到,若私自据为己有,必定为有知之人识破,付出代价. 所以,作者声明: 本人July对以上所有任何内容和资料享有版权,转载请注明作者本人July出处. 向你的厚道致敬.谢谢. ------------------------------------------------------------------------------

若干数据结构 && 算法面试题【四】(更新ing)

这是我的第三个面试题汇总. 想看之前的内容,请移步: http://zhweizhi.blog.51cto.com/10800691/1763237 ( 若干数据结构 && 算法面试题[一](更新完毕)) http://zhweizhi.blog.51cto.com/10800691/1775780 ( 若干数据结构 && 算法面试题[二](更新完毕)) http://zhweizhi.blog.51cto.com/10800691/1787562 ( 若干数据结构 &am

若干数据结构 && 算法面试题【三】(更新中)

这是我的第三个面试题汇总. 想看之前的内容,请移步: http://zhweizhi.blog.51cto.com/10800691/1763237 ( 若干数据结构 && 算法面试题[一](更新完毕)) http://zhweizhi.blog.51cto.com/10800691/1775780 ( 若干数据结构 && 算法面试题[二](更新完毕)) 另外,我的全部刷题代码都在这里: https://github.com/HonestFox/BrushQuestion

算法面试题

在上一篇博客中有原有提到分享一下面试题,最近也是才能新公司入职没多久,忙着熟悉环境,加上前不久出去玩了一趟(顺便写了篇游记,感兴趣的可以看一看).所以一直没时间整理博客,这段时间周末终于闲下来,就趁着周末就记录了几个面试过程中碰到的算法题. 本篇博客不打算介绍那些高逼格的算法(如winnow,bagging,ada boost等等),就讲讲最近在面试过程中遇到的算法题以及面试的时候给出的答案(出场率比较高的算法题我都列出来了,其他的就不说了).算法可以说是解决所有问题的基石.很多东西都可以转换为

九章算法面试题43 直方图内最大矩阵

九章算法官网-原文网址 http://www.jiuzhang.com/problem/43/ 题目 给出一个直方图(如图所示),求出所给直方图中所包含的最大矩阵面积.直方图可以用一个整数数组表示,如上图为[2, 1, 5, 6, 2, 3].每个直方块的宽度均为1.上图中包含的最大矩阵面积为10. 解答 如果对于每个直方块,找到从它开始往左边数第一个比它小的,和往右边数第一个比他小的,则可以确定出以该直方块为最矮一块的矩阵的最大面积.使用数据结构栈,栈中保存递增序列,从左到右依次遍历每个数让其

九章算法面试题31 子数组的最大差

九章算法官网-原文网址 http://www.jiuzhang.com/problem/31/ 题目 给定一个数组,求两个不相交的并且是连续的子数组A和B(位置连续),满足|sum(A) - sum(B)|最大(和之差的绝对值).例如[2, -1, -2, 1, -4, 2, 8],可以得到A=[-1, -2, 1, -4], B=[2, 8],最大差为16. 解答 预处理每个位置往左/右的最大/最小子数组,然后再枚举划分位置,求得所有MaxLeft[i] - MinRight[i+1]和Max

九章算法面试题37 主元素

九章算法官网-原文网址 http://www.jiuzhang.com/problem/37/ 题目 主元素(Majority Number)定义为数组中出现次数严格超过一半的数.找到这个数.要求使用O(1)的额外空间和O(n)的时间. 进阶1:如果数组中存在且只存在一个出现次数严格超过1/3的数,找到这个数.要求使用O(1)的额外空间和O(n)的时间. 进阶2:如果数组中存在且只存在一个出现次数严格超过1/k的数,找到这个数.要求使用O(k)的额外空间和O(n)的时间 解答 采用抵消法.一旦发

九章算法面试题40 不用除法求积

九章算法官网-原文网址 http://www.jiuzhang.com/problem/40/ 题目 给定一个数组A[1..n],求数组B[1..n],使得B[i] = A[1] * A[2] .. * A[i-1] * A[i+1] .. * A[n].要求不要使用除法,且在O(n)的时间内完成,使用O(1)的额外空间(不包含B数组所占空间). 解答 计算前缀乘积Prefix[i] = A[1] * A[2] .. A[i],计算后缀乘积Suffix[i] = A[i] * A[i+1] ..

九章算法面试题50 队列上实现Min函数

九章算法官网-原文网址 http://www.jiuzhang.com/problem/50/ 题目 ?在<九章算法面试题23 栈上实现Min函数>中,我们介绍了在栈上实现一个O(1)的Min方法.那么,如何在队列上实现一个Min方法? 要求,队列除了支持基本的Push(x) Pop()的方法以外,还需要支持Min方法,返回当前队列中的最小元素.每个方法的均摊复杂度为O(1) 解答 在九章面试题49<用栈实现队列>和面试题23<栈上实现Min函数>中,我们讲解到了如何用