闲聊算法考试的准备——总体思路

601 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

越来越多的公司在面试中都开始重视对算法的考察了,也有很多优秀的同学在面试中都被上机编程/手撕代码等环节卡住,这是很可惜的。那么,准备算法考试的过程中,有什么值得注意的点吗?

算法的魅力

我觉得算法的魅力,概括起来就是多题一解,一题多解。多题一解很好理解,会了一道题,可能就会了这一类题;一题多解,则是很多算法题往往都有多种解法,甚至结合你在其他领域的知识,可能帮你更快的解决一道算法问题。比如我们耳熟能详的斐波那契数列,有人通过递归解决,有人则通过数学知识解决:

通项.png 再比如这样的一个故事:一次谷歌的面试趣事 都体现了一题多解带给大家的启迪,乐趣。

不同于算法的算法考试

如果说算法的乐趣是启迪思维,把解题作为一种对艺术的追求,那算法考试恐怕就是纯比拼技术,需要你用最短的时间识别到这里想考察的是哪个数据结构/算法思维。那么,在算法考试中,有什么值得大家注意的点呢?

  • 五十而知天命————一个方法代码不超过50行 首先我觉得大家应该意识到的是,即使在参加算法考试,本质上仍是要让你敲一段代码出来。那么你平时写代码的好习惯这里可都千万别忘了展示出来,比如说,我们一般建议一个方法不超过50行。在大家写代码的过程中如果发现方法写了很多内容时一定要有意识的看下自己是否违背了单一职责原则(SRP),适当抽取方法,更全面地展示自己的代码能力。

  • 事不过三————不写三层嵌套的for、if 为什么不写三层嵌套的if条件,这是因为当你写出三层嵌套的if条件时,你应该考虑你这个方法可能圈复杂度就已经上去了,我们一般认为最大圈复杂度小于10较好,当写了很多if嵌套时,应该考虑通过方法抽取,或者卫语句来降低圈复杂度;那为什么不写三层嵌套的for循环(当然,我们一般说的for循环,循环次数都是题目传入的某个参数,而不是一个固定的常数值,固定常数不算for循环嵌套),是因为我们研究算法时,基本不研究比O(n^2)时间复杂度还复杂的算法,比如React的Diff算法,本来比较两棵树的变化是一个O(n^3)的时间复杂度,就因为"事不过三",它需要被优化成时间复杂度优于O(n^2)时,才不会为性能所累。

  • 先看数据规模 为什么不建议大家写时间复杂度高的算法,因为一般算法考试要求每个用例的执行时间为1秒(C++)或2秒(Java),这个时间内能进行的运算次数也就大概是10^8~10^9左右,或者可以看成是这样的一个对应关系:

数据规模时间复杂度要求
3*10^4 ~ 10^5优于O(n^2),比如O(nlogn)
10^6 ~ 10^7O(n)
10^9O(logn)

因此,有时大家可以这样理解,面试官往往会跟你有种“默契”,比如他希望你写段二分查找的代码,看看你二分查找理解的怎么样,他不会说请默写二分查找类问题常见模板,他会拿一道题给你,告诉你这数据有序,数据规模又到了10^9的级别,你看看怎么找到某个值?或者他想考你动态规划,可能题目中就总会有个“最”字。

  • 升级武器库————数据结构 如果说算法考试中怎么作弊,我想最好的方法就是对常用数据结构的各类操作牢记于心,可以在面对算法题时更快的分析出应该选择使用什么数据结构。举个最简单的例子,LeetCode 146题,大家看分析很容易发现:LRU = 双向链表 + 哈希表,那么用你习惯的语言,如何实现双向链表和哈希表,就是解题的关键,甚至如果你足够了解Java中的LinkedHashMap,你会发现,这里只需要覆写removeEldestEntry方法就够了,代码及其简单。如果这题要用JS去做,那就辛苦多了,可能这也是很多同学参加算法考试都会选用Java/Python/C++/Go这些语言,而很少选择JS的原因吧。

总结

这里列出了一些应对算法考试中的总体思路,比如,

  1. 不要因为是算法考试就不注意代码风格,要注意抽取长函数,有意识的控制圈复杂度,减少if嵌套;
  2. 即使没有思路准备用暴力法,也先看下数据规模;
  3. 平时准备时对常见数据结构及其常用方法的时间复杂度需要有基本了解; 希望能帮助在准备面试、算法考试的同学带来一些帮助,后面还会针对上面介绍的一些细节做更多详细的介绍,比如下一篇我们聊聊算法考试中最容易考的一种思维:递归,以及,为什么它的出场率这么高。