“
传说某个冕世冠打开视频聊天的窗口时,窗口那头正坐着位男人。他是宿舍里无论春夏秋冬都穿着同一件格子衬衫的唯一的人。
他面容温和,满面油光,略微凹陷的眼窝似乎刚刚熬了几个通宵,稍稍靠后的发迹线展露出绝顶的智慧。穿的虽然是衬衫,但却光鲜亮丽,一点没有水洗多年而产生的褶皱和破败。
他对人说话,总是满口的性能优化最优解时间空间复杂度,叫人半懂不懂的。
他一上线,窗口这头的冕世冠便看着他笑,客气的问道“知道面对对象吗”。
他也不正视镜头,低头思索了一会,抬头说道“知道一点”。之后便文绉绉的讲了一大段定义。
冕世冠微微点头,又突然问道“你会手写红黑树吗?” 嘤界僧抬起头,睁大眼睛说,“这…这太复杂了,我怎么会…”。
“红黑树是基本数据结构,也是基础算法。” 嘤界僧便涨红了脸,拳头在身下攥的紧紧的,青筋条条绽出,争辩道“我剑指offer刷了66题!…这不能算是基础算法么?”
接连便是难懂的话,什么“动态规划”,什么“KMP”之类,引得冕世冠也顿时放松了起来。
窗口内外充满了快活的空气。
”
以上故事纯属虚构,各位看官图一乐呵。不过也说明互联网大厂在面试校招生的时候,还是非常在意候选人的算法思维和代码能力的,特别是某条。
但是手撕代码这一块的确是最难准备的环节,对于绝大部分没有算法竞赛经历的同学来说,得依靠剑指offer和leetcode来快速培养算法能力。
剑指offer66题好说,但是leetcode上千道题,从头开始刷不仅效率低下,并且无法形成系统的算法思维,实际上也并推荐。
我个人对手撕算法的准备时间大概是在两个月,从去年暑假的7月到八月,最终顺利通过字节跳动的面试环节。
这里需要说明一点的是,字节跳动是十分看重算法能力的公司,一般技术三面基本必定会有手撕代码环节,每次面试视情况可能会有一道两道的算法题,每题大概会给5-20分钟,一般要求是要最优解。当然肯定是存在个体差异的。
那么到底应该如何准备手撕代码环节呢,该如何有效而系统的进行刷题训练呢?
01
面试考察的算法类型
1 原题
我自己感觉原题的概率还是挺大的,特别是剑指offer的66题更是如此。千万别小看这66题,这几十道题里面基本所有的算法类型都有包括在内,常用的数据结构,操作方式,常用算法思路都有不少的题。
如果真的能够充分理解这几十道题的最优解,我感觉其实已经形成基本的算法思维了。
另外,leetcode的原题也很常见,因为LC本身题量大,在里面出原题不是为了考倒你,而是检验你的刷题质量。
毕竟那些大公司面试官也不是傻子,知道你在面试前肯定会大规模刷题的。所以把刷过的题完全搞懂才是最重要的。
2 改编题
改编题就很显而易见了。改编题大多需要从基本的算法原理中找到处理的思维,然后结合实际题干进行性能优化,就能够搞定。
这里要记得一点的是,正常的算法考察不会故意刁难你(正常情况),也不会给过多的时间让你思考和敲代码。
所以遇到改编题不要想得太复杂,尽量要找到它的算法思维是什么。怎么说呢,透过现象看本质。我总结的改编题有以下几种思路:
1)新的数据结构,换汤不换药。比如最常见的排序算法的改编,原来是对数字进行排序,现在对链表排序等等。比较难一点的可能会遇到自定义的数据结构。但是算法本质不会变。
2)算法类型改编。
这里要说的就是一个比较大的范围,比如动态规划、贪心算法、递归、回溯和分治等等。这种是从算法大的类型上进行改编,很难用相同的套路去解题。
遇到这类题的关键就是要先弄明白算法核心。比如动态规划的状态方程,贪心算法的局部最优情况,递归回溯的边界判断,分治的子问题划分等等。这种类型的确比较难把握,怎么硕呢,每种类型的都来搞几道感觉感觉吧。
3)添加应用题背景。
这种题目看起来不难,但是难就难在对应用题背景的理解,需要去理解题意,然后考虑合适的数据结构和处理算法。这里面有数学建模的思维在里面,需要把一堆无用的信息剔除,筛选出有效的信息,然后才能选择正确的算法。
3 创新题
这类题考察的是你的扩展思维,如果说上面的题考查的是你的思维深度,这种题就是考察算法的广度。可能一看题目,完全没见过这种类型。但是算法本身其实不就是让计算机代替人脑进行高重复性的计算嘛。
首先你需要想到你应该去怎么算这个题,然后再换到计算机上,会发生什么问题(空间时间问题,运行效率,代码冗余等等),之后再想通过经典的算法原理来解决这些问题。
无论怎样,面试官总不可能让你在短时间内造一个新算法出来,解题思路肯定是有所依托的。
02
短时间如何系统刷题
说了那么多,还是得自己去刷题才行。那在时间不够的情况下,应该怎么去刷题,怎样才能形成系统的算法思维呢?
1 题型分类
我按照我个人的习惯,喜欢按照一种类型狂刷,然后再刷另外一种类型。一般常见的算法类型可分为:
- 数组、链表
包含基本排序算法、二分查找、链表的一系列操作。
- 栈、队列、堆
利用栈、队列互相实现,堆的使用
- 二叉树与图
主要是遍历算法和节点的计算:二叉树四种遍历方式、广度优先遍历(BFS)和广度优先遍历(DFS),节点到节点距离等等。
- 哈希表
使用标准库自带的模板或者函数就很简单了,一般会与其它数据结构相结合来提升时间复杂度。
- 字符串操作
字符串的操作也很多,本质上可以看作是数组的操作。另外字符串的一些匹配和寻求字串的算法还是非常具有思考价值的。KMP,马拉车等等。
- 递归
重点掌握边界判断条件。
- 回溯
重点掌握边界判断条件。
- 分治
重点掌握如何划分子问题。
- 动态规划
题太多了,可从一阶dp到二阶dp理解不同的状态方程。
- 贪心及其它
这个就很容易理解了,遇到贪心题应该要偷笑了。
2 高频热点多刷
这不多说了吧,Leetcode热题HOT 100。你值得拥有。
在不知道怎么刷的情况下,不如先刷起来。刷个题没那么多捷径,只有坚持刷起来了,才会形成自己的思维方式和学习习惯。
我建议是先按照类型刷,每个类型刷十几二十道。然后打混按照算法热度排序重新查漏补缺。
3 思路回顾
许多同学在一股脑刷了很多题之后,再看做过的题会发现忘了不少。可能大家都是这样的吧。我觉得是因为在刷题的时候过于心急,理解了大概就过了,或者类型做的太杂,没有留下印象。
我比较喜欢的方式是偶尔会重新看看曾经做过的题,就看题目然后想思路,再画一画步骤演进,没时间就不细敲了。这样可以增强一下思维记忆,之前理解过的东西,再回忆起来还是非常快的。
03
Leetcode题号
每个类型的题号我就懒得再单独列出来,Leetcode上的筛选已经做得非常好了。点进来,选择你想刷的类型,进去可以先挑战简单的题,然后再到中等,再到困难。题号在300之前的我都建议刷一下,后面的就看心情和精力了。
04
如何在面试中应对手撕代码
上面说了那么多,其实都是在面试前的准备工作。但是就算准备工作做得好,也不代表一定能够发挥得好。
毕竟面试存在太多的影响因素,你心理状态、身体状态以及和面试官的交流气氛都会影响你在算法题上的解题效果。因此在面试过程遇到手撕代码,我有以下几点建议:
1 保持冷静
情绪这种东西是很难掌控的,有时遇到没见过的题会慌张,有时又会被面试官的语气干扰了思考。但是只要在面试中,就还有机会。
在无法静心思考的时候可以拿出纸笔先自己想想思路,没必要一看题没见过就想着面试官能够给予提示。你在遇到问题时的思考态度反而更能打动面试官。
2 尝试交流
在短暂的思考过后,如果有了初步的思路可以试着跟面试官探讨。不必急于向面试官求证你的思路是否正确,更重要的是能够具有逻辑性和条理性的表达清楚自己的想法。
就算这个时候你的思路不正确,也会让面试官产生“你还挺有想法的”的判断,对你的印象也会加分。
3 展现足够的代码能力
手撕代码我认为是有两方面的考察的,一是算法思维,二是代码能力。很多时候我们都是有了代码能力,但是没有具备足够的算法思维。
所以当在没有想到完整问题的解法的时候,可以先尝试写些基本的处理代码,起码能够让面试官觉得你还是经常写代码的,只是可能这个时候状态不好,没有思路。
这不是取巧,而是尽可能的为自己争取一点机会吧。
4 要有思考过程
有时候手撕的代码可能刚好你刷过,甚至你一不小心还对代码很熟练。这当然是个好事情,但是你并不能显得自己是背过代码的感觉。
面试官一般都很反感这种行为,你这个时候可以先试着讲述整个算法思路,而不是急于在一两分钟内把这个算法整出来。
5 思维比把解决难题强
我经常听到同学说“那个谁谁在面试的时候,手撕代码题贼简单,所以面试才能过;不像我这么惨,出了一道leetcode困难的题,才把我刷了。”当然这种说法可能也有一定的原因吧,但是我情愿相信是思维能力差了点。
能做出难题的确很厉害,但是人家能做简单题不代表做不了难题。我的意思其实是在面试的时候,不要因为题目难度而放弃了思考。
我曾经在现场面试美团的时候,二面面试官就出了两道算法题,我一道都没写出来。但是我在面试的时候每一题都给出了好几种思路,虽然代码最终都没时间写完,但是也的确展示了自己的一部分能力吧。
最后在HR面的时候还听到二面面试官跟别的面试官在闲聊,说 “ 面了这么多人,也就那个×××(我的名字)表现还不错 ” 。我都惊了…本来以为肯定挂了。
综上所述,准备算法还是需要坚持+规划的。在面试前每天都需要刷点题找找手感,状态好呢就刷点难题,状态不好就刷点老题或者容易题。坚持下来,肯定会有所收获的。