开始新主题——征服面试算法系列
Posted 纵横千里,捭阖四方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了开始新主题——征服面试算法系列相关的知识,希望对你有一定的参考价值。
之前计划继续学习一些技术方面的问题,不过呢感觉自己在算法方面有一些心得,所以从6月开始开通了一个微信公众号,每天发布一篇,专门讨论算法的问题。这个专题与我的微信公众号保持同步。公众号会每天一篇,博客是不定期同步。
算法应该怎么学,这是所有程序猿都必须面对的问题,不同人的见解和感受会完全不一样,这篇文章,我就谈谈自己的看法。
我的核心观点是目前面试算法不是神秘的存在,而是有非常清晰的套路。我们的目标就是练好基本功,掌握套路。提高思维能力,从容应对未知问题。所以我的公众号的logo的含义就是“This is 算法的套路”。
1.算法的灵魂三问
算法的灵魂三问:算法有啥用?算法应该怎么刷?算法怎么才能一定过?
相信很多人都有这样的疑问,我很早之前也曾试图搞清楚这几个问题,随着经历得越来越多,自己的想法越来越清晰。
自从踏入互联网,每次换工作都受到算法的折磨,算法到底是无底洞还是有迹可循,如何有效准备算法一直是我在思考的问题,庆幸的是当我第二次换工作的时候,我就发现了面试时出算法题的规律。于是我就尝试自己整理笔记,就是从现在开始,你们见到的这一套。
我的观点是对的吗?不知道,但是经过几十次面试之后,我只能说亲测有效,不管好坏先写了再说。
第一问:算法有啥用
首先来看算法有啥用?很多人会说,我Java写得很好,我vue用得特溜,算法又不用,凭什么让我花这么多时间搞这些。算法为什么这么重要呢?我觉得吧,算法是所有程序员的共同语言。如果你一直做数据库,网络可能就和你没关系。如果你一直做CPU内核,可能永远不知道做前端的妹子讨论的VUE是啥东西,但是只要有代码就有算法在里,只不过是简单复杂的区别。
技术面试涉及三大块:项目经验,技术水平和算法功底。说得直白点,算法就是程序员岗位的敲门砖,如果想去大厂,如果想拿高薪,算法就必须过关。
第二问:算法应该怎么刷
程序猿都知道算法必须要刷,那该怎么刷呢?其实吧,很多人愁算法的时间比真正练习的时间更长。不知道大家在学生时代是否一直为如何学好英语犯愁过,整天各种疯狂英语,各种新东方,但是一直到毕业也没将英语真正学好。为什么呢?其中一个原因就是很多人天天喊着要学习英语,但什么时候认认真真做过呢?对于算法也一样,与其浪费大把时间各种纠结,不如一门心思将《剑指 offer》《程序员面试金典》等从头啃到尾。可以说任何一本,如果能认真啃两遍,算法基本就合格了。
还有一种情况是赌徒心理,提到算法内心就开始焦虑,就幻想着用一步登天来代替脚踏实地的努力。有的人心血来潮,随便选了一道,来考察自己算法到底行不行,十有八九是失败的,因为一道题可能涉及了hash,涉及了树,涉及了递归,结果你连怎么定义树节点都不知道 ,那怎么可能顺利过关呢,最后必然是打击自己。还有的人准备将leetcode全部刷完,从此彻底解决算法问题,然后从001开始向后刷,费劲几周没刷几道,信心没有了,不想刷了,甚至心生畏惧,最终算法成了过不去的痛。
还有一种是从0开始的心理,例如刷二叉树的线索化的题,发现自己递归不会,树不会,数组也整不清楚,于是就找了本厚厚的数据结构书,准备从0开始补。但是吧,大部分人在学过数据结构之后,就会对这类书没有特别的感觉,也不知道该看什么,不该看什么,于是最后就变成了浆糊,不了了之。
那算法应该怎么刷呢?
第一重:按照主题,循序渐进, 充分锻炼基本功。其实很多算法都是基于一个基本知识点的不同变换。做到学会一个知识点,掌握其变形,就可以刷掉一片的题。例如链表反转是一个基本的数据结构问题,但是面试的时候经常会考两两一组进行反转,指定区间进行反转,K个一组进行反转等等,如果基本的链表都不会怎么可能会K个一组呢?相反,如果我们将反转练好了,后面的变形就是反转的变形,我们一个问题训练了七八次,面试的时候再给一个新的变形,例如让你对链表进行奇偶反转是不是就很容易了呢?所以我们不能没有目标的乱刷,耗时间不说,效果还不好。
第二重:题目越刷越少,甚至很多题目都不用刷,看一眼就够了。其实算法面试的根基就是几种数据结构的典型应用,难一点的是几种算法(回溯,贪心,动态规划等)的应用。如果这些基本问题搞清楚了,给一个新题目,不管是场景题还是直接的算法题,我们都能很快定位其属于哪一类,该用什么模型来解决。之后就是常规的动态规划,树的递归等问题了。所以题目会越刷越少,甚至很多都不用刷了,面试的时候能够直接写出来。
第三重:能在项目中使用合适的结构,甚至自己也能出题。很多需要算法的场景一般都是项目中比较重要的功能了,这个是CRUD工作感受不到的。不信你看看JDK中JUC的底层实现,全是数据结构。而缓存之王的redis也是自己设计了一套数据结构。当我们对算法真正很了解的时候,我们可以解决更有水平的问题,写出更高质量的代码。
我们这套笔记后面会详细介绍怎么做,不过现在还限于第一级,后面两个希望有一天我们能做到。
第三问:算法怎么才能一定过?
算法怎么才能一定过?很多人内心希望通过某种方式刷题之后,算法一定能过,事实上这是不可能的,我们做的一切都是尽可能提高成功率。如果能将成功率从20%提高到80%就很好了。亲测证明,将高频面试题刷完,面试时出现原题的可能性大约40%,在此基础上的变形,大约40%。这就足够了。
如果说超出了80%,遇到某个神经病公司出了个很邪门的题,完全不会,怎么办呢?对于已经工作的我们,实在没有精力准备更多。我觉得三步:认命,走人。这公司很好吗?给的很多吗?加班很少吗?如果都不是的话凭什么让老子花那么多精力准备没用的算法。对不?
2.面试算法考察什么
在讨论怎么刷算法之前,我们必须先明白一个问题,为什么面试一定要有个算法,到底想考察什么。
如果一个真正想招人的公司,社招面试不是为了刷人而刷人,这与校招笔试有本质不同。网络经常看到的头条秋招算法汇总,阿里算法合集等等,这种都是针对校招而专门出的,就是为了刷人的,必然比较难,所以如果你是社招,准备去面试,最后不要刷这种题目。
社招更多是考察求职者的基本功如何。社招面试主要考察三点:
1.这个人的编程基本功是否扎实。基本功不扎实,写的代码可能到处是bug。
2.这个人的编码规范性如何。如果很混乱,边界处理不清楚,那做事肯定让人不放心的。
3.这个人思维能力如何,能够短时间内解决某些问题。这个反应的就是平时遇到问题是否能够很好的解决掉,而不是无能为力。
由此面试算法的特点是:
1.能够让面试者在20min左右完成,时间太久,面试官也没这么多耐心
2.基本数据结构的变型题,有思维含量,但不会太难太偏。
根据上面两点,我们可以认为不是所有算法都会考,刷算法也不是乱刷,而是应该更有针对性的刷,那这个范围是什么呢?
我们先回忆一下数据结构里有几种典型的结构,计算机算法里又学过哪几种经典的算法。想来想去,不过如下这些:
6大结构是数组,链表,栈,队列,树,Hash,图。
5大算法:分治,动态规划,贪心算法,回溯法,分支限界法
面试中手写算法,就是根据上面基本数据结构与算法的变形,而且这里的图是不用看的,因为图的基本操作和树是一样的,而图的定义和一些特性又过于复杂,不适合面试时短时间内写完,所以基本不用看。
同样,分支限界法用的也不多的,我们主要关注前四个就行了。
所以最后就剩下那几种典型的结构,这句话看似很简单,但是我们整个笔记的灵魂 。很多数据结构,例如树,能拓展出很多东西,后面我们会逐步展开。
3.如何正确的刷算法
目前大部分人准备算法的方式有那么几种:刷书,例如《剑指offer》《程序员面试宝典》等,刷视频教程,刷《leetcode》等大家公认的比较好的资源等。
我都曾干过,但是总感觉缺了点啥,后来我发现应该做到那么几点才行。
(1)刷题,自然要有刷题的套路
我们经常说要刷题,那怎么才叫刷呢?java等大部分的其他技术一般会了就真的会了,而算法题的一个问题是,你第一遍成功了,第二遍可能又忘了,总达不到会了就是会了的感觉。
这时候要马上再来一遍吗?我觉得比较好的方式是成功之后就继续进行下一个,例如研究其变形题,之后再研究下主题。完成之后再来一遍,虽然也会有问题,但是感觉会比上一次好很多,而且这时候也会发现一些规律和技巧,让自己对算法的理解更深更清晰。然后隔断时间再来一次。
当然,如果马上要面试,就要赶紧复习一下考察频率最高的题目,回顾一遍很有好处的,家里有粮,心里不慌。这时候一个好的材料就非常关键,如果不知道怎么选择,建议选大家都认可的,例如《剑指offer》《程序员面试宝典》等,只要刷完三遍,你一定不怕面试。
那对于一个题目该怎么刷呢?先自己做,如果半小时之内搞不定就应该去研究别人的解答,而不应耗费一晚上时间冥思苦想。因为没有思路就是没有思路,花费再多时间也想不出来。而题目的解答是有方法的,我们的重点是掌握这些方法,而不是完全自己造出来才是练习算法。例如LRU缓存设计的问题,如果没学不太可能想到利用链表和Hash来构造,如果完全自己写,一晚上可能只弄出问题很多的代码,而这个代码也是无法写给面试官看的,他想看的就是链表+Hash的实现。这种经过无数人检验过的内容,我们学会就行了,这才是刷题。
这时候又有一另一个极端了,一遇到不会的就抄答案,那我告诉你,刷了也白刷,面试时和没刷一样一样的。
(2)充分训练基本功,理清脉络
基本功是否扎实是面试考察的重点,刷的时应该充分训练基本功,而不是了解某个知识点,看个例子就草草完事了。面试考察的不是你仅仅了解基本的数据结构就行了,而是要真正做到活学活用,能够将题目写出来,甚至运行出来。例如二分查找也是一个出现频率很高的题目,基本要求是用循环和递归两种方式手写二分查找,但是面试经常出现的是其变形:如果有重复元素怎么办?如何用更优的方式解决平方根问题,如何在有序数组中寻找缺失数字,旋转数组中寻渣最小数字,统计一个数字在升序数组中出现的次数等等。如果后面的问题没有想清楚,就会感觉算法白刷了。
除了前面提到的链表反转和二分查找,几乎99%的题目都是从一个基本问题引申出来的 ,如果这些变换都练习充分了,那面试的时候,面试官出一个题目,例如如何实现前后两段分别反转,或者统计一个数字在升序数组中出现的次数的题,我们很快就发现这就是链表反转和二分查找的变形。如果每种情况我们都训练很多次了,面试再来一个,现场搞定也不难吧。
(3)由易到难,梯度提升对算法的认知
刷题的时候不应该大看特看基本常识,然后半做不做一两个题目就完事了。也不应该毫无目标随便找个题目就开始做,这个题目可能涉及字符串,递归,动态规划,排序等很多技术,如果上来就搞难的,只会在浪费几个小时之后感觉自己屁都不是,徒增焦虑,再无勇气看算法。例如leetcode第33题,搜索旋转数组这个题,如果没有提前练习过二分,很难想到如何优化,而想明白的时候,需要处理下标等问题时,也不知道可以直接套用二分的方法。直接套路快,还是现场自己想快呢,不不言而喻?
另外,对于大部分人来说,也不应该考虑《算法导论》这种难度比较高的书,如果你是硕士,或者数学相关专业的,可以看一下,但是推荐小白刷这本书的就该go die。
科学的刷题,应该努力做到从一个基本问题出发,然后逐步变换,逐步提高要求,最后再来解决了一些比较难的问题。这个其实挺难的,因为目前没有好的材料能帮我们做到这个。这也是我整理这套笔记的初衷吧,不过我只利用工作之余准备的,想做到很充分挺难的,如果你看的时候发现问题请和我说。
(4)一题多解,一法解多题,融会贯通
除了前面说的一个解题方法能处理很多问题,也经常有一个题目是有多种解法的情况,将其梳理清楚,会让我们对算法的理解更加深刻。当然解法有好坏和难易之分,很多面试官是喜欢听求职者对这个题目进行充分分析的,如果上来就写,就会认为这个人只会刷题而已,反而会心生排斥。如果能对一个题目的多种解决方法进行完整的分析,最后手写出最佳的那个,面试官定会青睐。
一法解多提是什么意思呢 ,就是一个解决问题的方法可以同时解决掉大量相同场景的问题,例如上面提到的二分查找能解决求平方根的优化问题。快速排序能用来寻找第K大的元素等等,这种例子数不胜数。
(5)算法是如何应用的呢?
学习算法,很多人的疑问一直是算法到底有啥用?好好的写代码不香吗?为什么要花心思研究这些伤脑筋的东西?一般的CRUD业务自然不需要高级的数据结构和算法,但是真正要用的时候,要解决的问题已经比较高级了,所以高级代码很多都是大牛们已经写好了,我们平时遇不到而已。例如java里的JUC并发框架里就封装了大量的数据结构。redis自身设计了String,HashMap,List,Set,Zset等,内部实现了ziplist,快表,跳表等多种数据结构。而常用的服务治理工具zookeeper,本身就是一个巨大的树形结构组织起来的。很多应用型框架例如Spring,netty等都会根据自己的需要设计相关的数据结构。再比如apache的common包,Google Guava等,为我们提供了丰富的工具,使得我们察觉不到算法的存在。而一般我们要解决一些高级问题的时候,算法几乎是绕不开的问题。
在这套笔记里,我努力介绍那些重要的算法是如何使用的,或者一些典型的应用是如何使用算法的,这个非常难,是个长期活。如果你又好的想法也请和我说。
最新文章请关注我的公众号:
以上是关于开始新主题——征服面试算法系列的主要内容,如果未能解决你的问题,请参考以下文章
扎实打牢数据结构算法根基,从此不怕算法面试系列之006 week01 02-06 循环不变量