本人以前大概搞过半年的算法,不是什么大佬,学得也不怎么样,一般般。leetcode只刷了200左右(没有水题),leetcode简单、中等级别的题目大部分都可以做。大部分公司的笔试题也还行,当然了像字节、腾讯那种就太难了,根本顶不住,面试遇到的算法题一般也能答得上来(其实也没啥面大厂的机会),偶尔也会有失误。
先说一说刷leetcode的前提,建议不要完全零基础就一股脑的去刷题,如果你啥都不会临时突击直接上去刷题的话emmmm,不出意外的话你只会看着别人的题解刷题,看了别人的思路你也不一定会写,就算写出来了很快你就忘了。所以要对常见的算法有一定的基础,常见的模型较为了解之后再去刷题。比如常见排序(冒泡、快排、插入、堆排、归并…)、双指针(快慢指针、左右指针)、贪心、二分(二分查找)、搜索(dfs、bfs、各种剪枝、回溯思想)、动态规划(各种子序列、各种子串、常见的那几个背包问题)、前缀和、树的常见操作(各种遍历、各种树的性质、最小生成树)、图(最短路、常见性质)、常见数据结构和集合类(链表、队列、栈、map、list、set、并查集都得会用),这些基础知识不求你精通,不会写代码都行,但是你总得知道这些知识咋回事。当然了如果你是搞过acm竞赛之类的大神,这些可以都跳过,直接上手。关于怎么学习这些基础的算法知识,个人建议新手看视频或者看带图的博客,看博客文字的话可能看不懂,直接就劝退了,当然有点算法底子之后直接看博客也是没问题的。
那么该刷leetcode的哪些题,截止到目前为止leetcode已经有1300+的题目了,其实不用全部都去刷(对大部分人来说也没那个时间),刷那些比较热门的就ok,或者按照各个知识点去刷。所有的题目分简单、中等困难三个等级,根据我个人的经验来看,简单和中等的题目出现的频率是最高的,困难级别的很少见,所以建议刷题数量中等>简单>困难。关键是困难的太难了,一道题目可能两三天你都还搞不出来,一来怕把你劝退了,二来性价比不高,所以困难的刷一小部分就好了。有一些很简单没有用到任何算法的模拟题、博弈论、最大流、特别复杂的树和图操作,一般来说这些知识笔试面试都不会考。so刷题具体要怎么刷呢,看完题目之后不要直接看别人的题解,先自己思考,正常情况下很少会马上就想到最优的解法,要从暴力、爆搜开始思考,然后再进一步思考怎么去优化复杂度。常见的思考方向如下:
我的表达能力不太好,可能上面的描述的思考方向有点不太好理解,也是我个人的总结,如果有不对的地方,请多多指正。顺带说一下,leetcode上面的题目的测试样例并不是特别完善,有一些题目样例偏小,用暴力做也是可以ac的,所以千万不要因为单纯的ac去刷题,我看到一些用暴力ac了题目在评论区炫耀的,还引以为荣喊着ac了就行,emmmm自己体会吧。做完题目之后怎么去利用题解?很多题目是不止一种最优解的,会存在多种最优解,上面已经说过了,不要为了ac去刷题,争取要把多种解法都搞明白(尽量看那种热度高的、带图的)。看完他们的思路、最优解法之后,要思考最初的暴力法是怎么过渡到最优解法的,不要只是单纯的把这道题目的最优解法背下来,毫无意义。暴力法 ----> 最优解法,中间的这个过渡过程才是最重要的,这样学才能体会到学算法的快落,就像学数学一样,记住这个公式没有什么用,要知道这个公式是怎么从已知条件推出来的,这才是最重要的。
最后解答下一些常见的疑问。
Q:为了笔试面试什么时候准备算法比较好?
A:越早越好,因为算法不同于其他的知识,数据库、语言基础这些东西临时突击再不济也能背一点下来。可是算法是不能突击的,你早一点准备的话,在面试高峰期的时候就可以把时间放在复习其他知识上面了。从笔试情况来看,大部分公司基本上都是必考编程题。所以越早准备越好。
Q:用什么语言写算法代码比较好?
A:习惯了用什么语言就用什么语言,语言只是工具,用啥都行,不过能用c++就尽量用c++吧,因为大部分题解用的都是c++。像java之类的太依赖快捷键了,一些笔试不能用本地ide,没有快捷键连个包都导不出来,而且有一些宣讲会是现场笔试的,手写代码非常影响心情和速度,平时有事没事可以多去练练手写代码。
Q:题目读不懂怎么办?
A:其实专门针对笔试面试的话题目读不懂这种情况还是比较少的,那些搞竞赛的遇上压轴题题目读不懂很正常,笔试面试一般都是单刀直入。笔试可以结合样例仔细地多读几遍题目,慢慢的抠字眼,一般都能读懂。面试的话有时候是面试官描述得不太好,可以去问面试官,让他说得再详细一点,让他多给个样例,让他举个例子,然后你再把你的理解和面试官说一下再次确认。切记题目没完全搞明白就直接下手写代码,这就和开发需求还没搞明白就开始code是一样的。
Q:读完题目之后一点思路都没有怎么办
A:先把最简单最直接的暴力做法想出来,然后再慢慢的去优化,参考上面的思考方向。要知道除了大神,很多题目都不会让你一眼就看到最优的解法,都是慢慢思考出来的,这个从暴力法到最优解法的思考过程才是算法的乐趣。就算你想不到最优的解法,能优化一点是一点,很多公司的判分就是看你能过多少样例。面试也是同样的道理,先说出暴力法,然后慢慢过渡到最优的解法,体现出这个思考过程,面试官会觉得你有思考深度。换句话说,哪怕这题你不会,你把暴力法说出来,也总比直接放弃跟面试官说不会好啊。
Q:有思路但是代码写不出来怎么办
A:其实对算法而言,思路才是最重要的,写代码是最后一步,也是最简单的一步。你可以去问一下身边搞acm的同学,他会认可这句话的。你要是能把思路说得清清楚楚明明白白,肯定可以把代码写下来,写不出来是因为平时这类代码写得少,没有去总结。平时要多去看看模板,多去看看大神写的代码,像二分法是有模板的;链表如果要操作头部通的话常可以给头部加个空节点;一些要处理头尾的数组可以从1开始;二维数组地图类的题目要遍历多个方向可以把方向存到数组里例如int[][] next = {{0,1},{0,-1}…};还有字符串处理、保留n位小数、括号类()的处理、回文串等等这些代码都是有模板有技巧的。这样可以让你的代码更加简短容易让人读懂,也不容易出错。每一次刷题ac完之后都去看看大神的代码,平时多练习、多总结、多交流。慢慢的你就可以写出简单高效不容易出错的代码了。
Q:思路是正确的,但是代码写出来是错的怎么改
A:打断点debug,认真查看各个变量的变化,一步一步往下点,这个过程就是你的算法思路,如果还是看不出来,就拿草稿纸画图,把变量记下来。这个起步是很痛苦的,我刚学dfs的时候调试八皇后的代码调了一个晚上。
Q:思考的时候总是漏掉一些情况,代码总是缺斤少两,不能一次ac怎么办
A:其实不光是笔试面试,搞竞赛的也是一样,总是罚时了好多次才能ac。笔试的时候可以马上评测告诉你通过了多少样例还好,如果是不马上告诉你评测结果只让你提交代码的话,本来能ac的只能过80%、50%多难受。建议不管是练习还是笔试,每一次提交代码之前都要想出多种特殊样例进行多次自测,每一次都告诉自己只有一次机会,同时也要注意样例范围和格式,如果是牛客网平台笔试的话一定要先自测再提交代码。怎么想出特殊样例呢?举个简单的例子假如让你写一个排序算法,可以自己想出特殊的样例,例如:[],[3],[6,6,6],[3,4,5],[5,4,3],[1,-1,1,-1],[0,0,0],[min,min,min],[max,max,max],这些都算是特殊样例,多去测一测,如果代码有问题很快就可以看出来了。
最后奉献一下一位大佬的由数据范围反推算法复杂度以及算法内容的总结
资源来源
一般ACM或者笔试题的时间限制是1秒或2秒。
在这种情况下,C++代码中的操作次数控制在 107107 为最佳。
下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:
n≤30n≤30, 指数级别, dfs+剪枝,状态压缩dp
n≤100n≤100 => O(n3)O(n3),floyd,dp
n≤1000n≤1000 => O(n2)O(n2),O(n2logn)O(n2logn),dp,二分
n≤10000n≤10000 => O(n∗n√)O(n∗n),块状链表
n≤100000n≤100000 => O(nlogn)O(nlogn) => 各种sort,线段树、树状数组、set/map、heap、dijkstra+heap、spfa、求凸包、求半平面交、二分
n≤1000000n≤1000000 => O(n)O(n), 以及常数较小的 O(nlogn)O(nlogn) 算法 => hash、双指针扫描、kmp、AC自动机,常数比较小的 O(nlogn)O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa
n≤10000000n≤10000000 => O(n)O(n),双指针扫描、kmp、AC自动机、线性筛素数
n≤109n≤109 => O(n√)O(n),判断质数
n≤1018n≤1018 => O(logn)O(logn),最大公约数