算法面试之弊


原文来自《左耳听风:传奇程序员练级攻略》第6章

我反对纯考算法的面试。能够解决算法问题并不意味着有能力在工作中解决其他问题,这就好比奥数能手不见得能解决实际数据库问题。
微博上曾有一道“找出无序数组中第二大的数”的题目。关于解法,几乎所有人都使用了 O(n)的算法。其实,对于接受过应试教育的人来说,不用排序算法而使用 O(n)算法是很正常的事,我甚至认为O(n)算法是这个题目的标准答案。可见,我们太相信标准答案了。你的思维习惯于依赖某个标准答案,但是标准答案会阻止你思考,从而使你的思维固化下来。

试想一下,如果在实际工作中遇到这样一个问题,我们会怎么做?我一定会先分析这个需求,因为担心需求未来会改变。今天的需求是找第二大的数,明天可能找第四大的数,后天可能找第一百大的数,需求变化是很正常的事。分析完需求后,我会很自然地去编写出找第K大的数的算法–难度一下子就提高了。很多人可能会认为,实现查找第K大的数的需求是一种“过早#展”的思路,但实际上并不是,在实践中我们写过太多这样的程序了你一定不会设计出 Find2ndMaxNum(int* array,int len)这样的函数接口相反,你应该声明一个叫作 FindKthMaxNum(int* array, int len,int kth的函数,把2 当成参数传进去。这是最基本的编程方法,在数学里叫代数。
性能等非功能性需求通常不是最重要的。在解答算法题时,我们太注重算法的时间和空间复杂度了,想要在时间和空间方面找到最优解。受这个思维影响,我们只会机械地思考算法内部的性能,而忽略了算法外部的性能。
对于“从无序数组中找到第K个最大的数”这个问题,使用 O(n)的线性算法很常见。事实上,STL 中的 nth element 函数也可以用来求解第飞大的数。它利用快速排序的思想,从数组S中随机选取一个元素X,并且将数组分为 Sa 和 Sb 两部分。Sa 中的元素大于等于X,Sb 中的元素小于 X。这时有两种情况: Sa 中元素的个数小于 K,则Sb 中第 K-Sal个元素即为第K大的数;Sa 中元素的个数大于等于K!则返回 Sa 中的第K大的数。其实现复杂度接近于 O(n)。
当谈到性能时,通常每个人都会问:请求量有多大?如果FindKthMaxNum0的请求量为 m 次,则使用 0()复杂度算法的最终复杂度为 0(n*m),这一点学术派永远想不到。

根据上面的需求分析,有软件工程经验的人的解决方案通常是:将数组从大到小排序;找到第 K 大的数只需访 array[K]
一次排序的复杂度只有 0(n*log(n)),之后m次对FindKthMaxNum0的调用都只有 O()的复杂度,整体复杂度反而成为线性的。
实际上,上述解决方案还不是工程化的最佳解决方案,因为在真实业务中数组中的数据可能会发生变化。如果使用数组排序,一旦数据有更改,会强制对数据重新排序,这将非常耗费资源。因此,如果有许多插入或删除操作,还可以考虑使用 B+树。

工程化的解决方案具有以下特点:

  • 非常容易扩展。因为数据已排序,可以轻松应对各种需求,例如找出从第 K1大到第 K大的数。
  • 规整的数据会简化算法的复杂度,从而改善算法的整体性能。e
  • 代码变得清晰,且易于理解和维护!

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注