0
点赞
收藏
分享

微信扫一扫

笔试/面试/leetcode刷题经验之谈

有人相爱,有人夜里开车看海,有人leetcode第一题都做不出来。这是一篇关于leetcode刷题的一点点小心得,如果对你有帮助的话,欢迎点赞转发。

前言

本人以前大学的时候搞过一点算法,但不是什么大佬,学得一般。leetcode只刷了200左右(但都不是啥水题),leetcode简单、中等级别的题目大部分都可以做。大部分公司的笔试题也还行,当然了像字节腾讯一些太难的题目就有点顶不住了,面试遇到的算法题一般也能答得上来,虽然偶尔也会翻车。

刷题前的准备

其实很多人在这方面还是比较疑惑的,可能啥都没整明白就冲题解区了,建议不要完全零基础就一股脑的去刷题。因为如果你啥都不会直接上去刷题的话emmmm,不出意外你只会看着别人的题解刷题,看了别人的思路你也不一定会写,就算写出来了很快你就忘了。所以要对常见的算法有一定的基础,对常见的模型较为了解之后再去刷题,这样效果会更好。

比如常见排序(冒泡、快排、插入、堆排、归并…)、双指针(快慢指针、左右指针)、贪心、二分(二分查找)、搜索(dfs、bfs、各种剪枝、回溯思想)、动态规划(各种子序列、各种子串、常见的那几个背包问题)、前缀和的常见操作(各种遍历、各种树的性质、最小生成树)、(最短路、常见性质)、常见数据结构和集合类(链表、队列、栈、map、list、set、并查集都得会用)。

这些基础的知识不求你精通,代码不熟练也ok,但是你总得知道这些知识咋回事。当然了如果你是搞过acm竞赛之类的大神,这些可以都跳过,直接上手。关于怎么学习这些基础的算法知识,个人建议新手看视频或者看带图的博客,看博客文字的话可能看不懂,直接就劝退了,当然有点算法底子之后直接看博客也是没问题的,适合自己的学习方式才是最好的学习方式。

该刷哪些题

截止到目前为止leetcode已经有1300+的题目了,其实不用全部都去刷(对大部分人来说也没那个时间)。刷那些比较热门的题目就可以了,或者按照各个知识点去刷,不用全部去刷,特别是那些水题,太浪费时间了。

所有的题目分简单中等困难三个等级,简单和中等的题目出现的频率是最高的,困难级别的很少见,所以建议刷题数量中等>简单>困难。关键是困难的太难了,一道题目可能两三天你都还搞不出来,一来怕难度把你劝退了,二来投入的时间和收益性价比不高,所以困难的刷一小部分就好了。

博弈论、最大流、特别复杂的树和图这一类的题目,一般来说这些算法笔试面试都不会考,一般来说这些题实在是太难了,毕竟不是每个同学都打过acm的。还有一些是代码量比较大的,太浪费时间了,笔试、面试的时候没那么时间让你去写代码,面试官也要干完公司的事下班的。

从个人经验来看,小公司或者外包不怎么考算法,就算是考,也就随便问个排序,大概率很多面试官自己都不会算法,哈哈哈哈。一般牛逼点的公司喜欢考排序二分dp双指针贪心之类的,大厂几乎都是比较复杂点、经典的dp

怎么去刷题

大道理我都懂,问题是具体怎么去刷题呢?这里有两种方法,一种是多题一解,顾名思义,就是多道题一种做法,刚学完一个算法比较适合这种方法,可以通过做题快速强化刚学的算法。比如刚学完dp,就可以去做leetcode上带dp标签的题目,短时间内很快就会对dp有感觉。

相对来说另外一种做法一题多解就比较难一点,一题多解即一道题目用多种解法,往往很多题目都是有很多种解法的,这个比较合适有算法基础的朋友。在面试的时候面试官往往也会比较喜欢求职者有一题多解的能力,因为考你算法题也是希望筛选出优秀的人才,而不是只会背题目的人。

看完题目之后不要直接看别人的题解,先自己思考,正常情况下很少会马上就想到最优的解法,要从暴力、爆搜开始思考,然后再进一步思考怎么去优化复杂度。常见的思考方向如下:

  • 枚举时可以留意一下枚举的范围是否具备单调性,可以考虑用二分;
  • 双重循环看一看是否可以用双指针或者前缀和之类的算法优化;
  • 一些要查找的O(n)复杂度的部分是否可以用map、set、树之类的的数据结构去优化;
  • 括号、回文之类可以思考一下能否用栈去处理;
  • 一些暴力搜索的部分看一看是否存在无法到达的状态可以考虑剪枝;
  • 涉及到字符串的各种操作尝试一下区间dp;
  • 暴力搜索时看看是否有可重复利用的状态用map或数组把状态存起来(记忆化搜索),然后再看看是否可以用dp;
  • 一些枚举的范围很大一看就知道不可能ac可能思考一下是否可以直接推出结论或数学公式(贪心的思考方向);
  • 如果遇到一些需要反复计算的部分,可以考虑先计算好然后把它存起来,下次直接拿来用(前缀和思想);
  • 一些题目的条件和经典题型很像的可以考虑直接套用经典题型的做法(例如背包模型模型,各种子序列模型);
  • 一些常见的操作可以看看有没有现成的类或方法可以直接拿来用(例如java大数、排序、字符串反转、拼接等常见操作);
  • 一些数字的操作可以看看是否涉及到二进制,可以尝试下异或、&这些操作;
  • dp看看每个状态的上一次状态,如果发现浪费了大量的状态,可以考虑优化空间,降低空间复杂度(例如斐波那契,每一个的状态只和前面的两个状态有关);
  • 可以通过题目的样例范围反推出算法的时间复杂度,从而考虑题目大概可以用什么算法。
  • 如果题目的某个部分需要用O(lgn)的算法复杂度,尝试把思考方向放到二进制、二分或各种树上。

表达能力不太好,可能上面的描述的思考方向有点不太好理解,这也是我个人的总结,不一定百分百对。顺带说一下,leetcode上面很多题目的测试样例范围并不是很大。有一些题目样例偏小,用暴力做也是可以ac的,所以千万不要因为单纯的ac去刷题,单纯追求过题数就没有了。

如何合理地利用题解

如果题目ac了,你需要去题解区看别人的思路,说不定别人的思路比你的更巧妙、更快。就算是思路都一样,每个人的代码也不一样,可能别人的代码比你的更简洁。特别是高赞的题解,总有值得你学习的地方,水题除外。

其实很多题目其实都是不止一种最优解的,会存在多种最优解,上面已经说过了,不要为了ac去刷题,争取要把多种解法都搞明白(尽量看那种热度高的带图的,会比较容易看懂)。

看完别人的思路、最优解法之后,要思考最初的暴力法是怎么慢慢过渡到最优解法的,不要只是单纯的把这道题目的最优解法背下来,毫无意义。就像学数学一样,记住一个公式没什么用,要知道这个公式是怎么推导出来的,暴力法 ----> 最优解法。这个思考过程才是最重要的。

常见的问题

Q:为了笔试面试什么时候准备算法比较好?

A:越早越好,因为算法不同于数据库、语言基础之类的知识,这些东西临时突击再不济也能背一点下来。但算法是不能突击的,你早一点准备,在面试高峰期的时候就可以把时间放在复习其它知识上了。从笔试面试来看,大部分稍微好点公司基本上都是必考算法题,所以越早准备越好。

Q:用什么语言写算法代码比较好?

A:习惯了用什么语言就用什么语言,语言只是工具,用啥都行,不过能用c++就尽量用c++。因为大部分题解用的都是c++,看题解会方便很多,而且其它语言比较慢,笔试的时候运气不好就可能被卡住了。

像java之类的太依赖快捷键了,一些笔试不能用本地ide,没有快捷键连个包都导不出来,而且有一些宣讲会是现场笔试的,手写代码非常影响心情和速度,平时有事没事可以多去练练手写代码

Q:题目读不懂怎么办?

A:其实专门针对笔试面试的话题目读不懂这种情况还是比较少的,如果搞竞赛遇上比较难的题目读不懂很正常。笔试面试一般都是单刀直入,像leetcode上的题目都是直接告诉你题目意思,不和你绕弯子。笔试可以结合样例仔细地多读几遍题目,慢慢的抠字眼,一般都是能读懂的。

如果面试的话有时候是面试官描述得不太好,可以去问面试官,让他说得再详细一点,让他多给几个样例、举例子。然后你再把你的理解和面试官说一下再次确认题意

切记不要在题目没完全搞明白的时候就直接下手写代码,这就和开发需求还没搞明白就开始code是一样的,最后会因为需求不清晰推倒重来。不要怕这样会给面试官留下不好的影响,反而这可能是个加分项,因为在职场工作的时候这种因为沟通不到位而导致工作效率低下的事情实在是太多了。

Q:读完题目之后一点思路都没有怎么办

A:先把最简单最直接的暴力做法想出来,然后再慢慢地去优化,参考上面的思考方向。要知道如果不是大神,很多题目都不能一眼就看到最优解法(除非是水题或者做过)。都是慢慢思考出来的,这个从暴力法到最优解法的思考过程才是算法的乐趣,才是灵魂。就算你想不到最优的解法,能优化一点是一点,很多公司的笔试判分机制就是看你能过多少样例。

面试也是同样的道理,先说出暴力法,然后慢慢过渡到最优的解法,体现出这个思考过程,面试官会觉得你有思考深度。如果直接说出最优的解法,面试官可能会觉得你是背的题目,或者要是你直接说出最优解法后面试官问你这个解法的原理,你答不上来不是很尴尬。换句话说,哪怕这题你不会,你把暴力法说出来,也总比直接放弃跟面试官说不会好啊,至少态度摆在这了。

Q:有思路但是代码写不出来怎么办

A:其实对算法而言,思路才是最重要的,写代码是最后一步,也是最简单的一步,代码只是一个表达方式。你可以去问一下身边搞acm的同学,他会认可这句话的。你要是能把思路说得清清楚楚明明白白,肯定可以把代码写下来,写不出来是因为平时这类代码写得少,没有去总结。

平时要多去看看大佬的模板,像二分法是有模板的;链表如果要操作头部通的话常可以给头部加个空节点;一些要处理头尾的数组可以从1开始;还有字符串处理括号类的处理、回文串等等这些代码都是有模板有技巧的。这样可以让你的代码更加简短容易让人读懂,也不容易出错。每一次刷题ac完之后都去看看大神的代码,平时多练习、多总结、多交流。慢慢的你就可以写出简单高效不易出错的代码了

Q:思路是正确的,但是代码写出来是错的怎么办

A:先想出简短特殊的样例,用输出打印数据,快速查看代码错误的地方,也就是所谓的print大法。如果找不出来就只能打断点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++代码中的操作次数控制在 10^7 为最佳。
下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:

n≤30 => 指数级别, dfs+剪枝,状态压缩dp
n≤10^2 => O(n^3),floyd,dp
n≤1000 => O(n2),O(n^2logn),dp,二分
n≤10^4 => O(n∗n),块状链表
n≤10^5 => O(nlogn),各种sort,线段树、树状数组、set/map、heap、dijkstra+heap、spfa、求凸包、求半平面交、二分
n≤10^6 => O(n), 以及常数较小的 O(nlogn)算法, hash、双指针扫描、kmp、AC自动机,常数比较小的 O(nlogn)O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa
n≤10^7 => O(n),双指针扫描、kmp、AC自动机、线性筛素数
n≤10^9 => O(n√)O(n),判断质数
n≤10^18 => O(logn),最大公约数

总结出自***大佬。

感谢大家的观看,如果觉得对你有帮助的话,点个赞吧,祝愿大家都能找到一份好工作!

说明:本文限于篇幅,故而只展示部分的面试内容,完整的leetcode面试学习文档小编已经帮你整理好了,有需要的朋友点赞+关注私信评论留言哦

举报

相关推荐

0 条评论