什么准备都别做,直接开始刷吧。从easy开始。
对于每道题:
我从2sum不会写,到通过狗家和脸家实习的电面。走了很多弯路。现在回过头来看,埋头猛刷(LeetCode),其实不一定是最有效的方式。尤其是Google,面试过程中,特别强调交流。所以我们平时刷题就需要安装四个步骤来要求自己。
第一是communication。一定要和考官交流思路,还有就是不明白的地方一定要问清楚。
第二是problem solving,要展示自己的解决问题的策略。比如选数据结构什么的,一定要讲明白为什么要用各种算法和数据结构。
第三是coding。在前面两步的基础上,写出来干净正确的代码。
第四是testing,一定要回去验证自己的代码。这个过程中可以分析一下代码的复杂度。
怎么去练以上的步骤,有详细过程解析的入门教程就能起到很重要的作用。我在刷题的中期,在某论坛的推荐下,接触到了educative的课程。特别有帮助。
这些课程是我在准备算法面试时,自己跟着学完且颇有成效的。作为转码的选手,这些课程帮我梳理了主要考察的内容,更让我有了系统性的知识框架。
这些课程主要还是适用于美国的面试环境,咱们平时刷题的时候不太注重英文交流方面的训练,这样在面试的时候哪怕会做题,但不能让面试官知晓意图,面试结果也不会突出的。
所以平时不仅仅要能写出来代码,还得能用英语表达清楚每一步的意义。这些英文课程的表述能让大家早点习惯各种术语的表达。
之前这些课程都是买断的,现在也只能一年之内有效了。单独购买这些课程可以,但其实还是推荐直接按年来学,一年之内把下面这些课程学完是没有问题的。
如果有需要的小伙伴,这个网站的课程,能用ZHIHUEDU-10的coupon code来享受按年或是按月收费模式的额外九折的优惠。
我现在大概介绍一下我用过的几门课:
Grokking Dynamic Programming Patterns for Coding Interviews. 这门课主要是针对DP,大部分的题都用递归,Top-Down, Bottom up三种方法解一遍,来龙去脉讲得非常清楚。特别适合DP有一定感觉,但又不能融汇贯通的小伙伴。
Data Structures in Java: An Interview Refresher.这一门课是把数据结构里面的基础数据结构都用java实现了一遍,对于用java的同学特别有帮助,java的基础在刷题的过程中,还是要必须掌握的。从复杂度开始,Arrays,LinkedLists, Stacks/Queues, Graphs, Trees, Trie, Heaps, Hash Tables,全都实现了一遍。而且还有配套的基础LeetCode题。是一个入门的很棒的教程。
Coderust: Hacking the Coding Interview.这门课精选了八十左右道题,每道题都有详细的讲解,对我最有帮助的地方是里面的代码运行步骤,特别详细,对代码的理解特别有帮助。
Grokking the Coding Interview: Patterns for Coding Questions.这门课程是一个总结提高的课程,它把算法面试的遇到的题型分成了各种模式,每类题各个击破。比如最经典的sliding window模式,Two pointers模式,快慢指针模式,合并intervals模式,cyclic sort模式,in-place翻转链表模式,树上的BFS,树上的DFS,双Heaps模式,subsets模式,二分法变种,Top K模式,多路模式(K-ways),0/1背包,拓扑排序。
Grokking the Machine Learning Interview 对机器学习面试的内容做出了详细的梳理,对大家准备机器学习的面试以及team match有很大的帮助。
对于刚刷题的小伙伴来说,很多题目就是一个很抽象的描述,刷了几百道题,还是不知道为啥要刷这些题,也不能很好的放到实际的生产生活环境中。针对上面这些知识和场景之间的gap,educative推出了下面的这一系列课程,希望能帮助大家从实际生产环境的角度去解答算法问题。
下面这个系列是新出的课程:通过大公司真实常见问题来破解算法面试。
下面是用Java语言的:
还提供了Python:
C++:
以及JS版本:
当然还有一名最出名的:Grokking the System Design Interview.鼎鼎大名的system design课程。
以及OOD课程,Amazon面试喜欢考察OOD的内容。
其他还有很多课程可以选择,可以去搜索。
刷题之前,了解一下基本的数据结构,然后了解一下都有哪些常见的题目类型。刷的时候第一二遍最好按照类型来刷,这都是过来人的经验了。
关于题目类型的回答,大家可以看看这个回答。刷之前先过上一下总会有收获的。
刷题要坚持,加油!
搬运过来方便大家阅读:
这个课程来自于educative,是一个美国的算法面试方面很出色的网课平台。
这门课程是一个算法总结提高的课程,它把算法面试中可能遇到的题分成了各种模式,每类题各个击破。
如果想买订阅(Subscriptions)的小伙伴,则可以用ZHIHUEDU-10(必须一模一样输入)的coupon code来获取额外九折的优惠按年和按月均适用。
下面这个系列是新出的课程:通过大公司真实场景问题来破解算法面试。
下面是用Java语言的:
还提供了Python:
C++:
以及JS版本:
比如有最经典的sliding window模式,Two pointers模式,快慢指针模式,合并intervals模式,cyclic sort模式,in-place翻转链表模式,树上的BFS,树上的DFS,双Heaps模式,subsets模式,二分法变种,Top K模式,多路模式(K-ways),0/1背包,拓扑排序。
需要的小伙伴就去来一波吧!
他家最最出名的还是这门Grokking the System Design Interview, 但凡提到准备系统设计,这门课都上入门必推的:
以及OOD: Grokking the Object Oriented Design Interview
这门机器学习面试指南是这个系列最新的课程:
目前市面上机器学习面试相关的课程比较少,这门课程应该非常值得!
方便大家阅读,我把内容也贴出来放在这个回答下:
滑动窗口类型的题目经常是用来执行数组或是链表上某个区间(窗口)上的操作。比如找最长的全为1的子数组长度。滑动窗口一般从第一个元素开始,一直往右边一个一个元素挪动。当然了,根据题目要求,我们可能有固定窗口大小的情况,也有窗口的大小变化的情况。
下面是一些我们用来判断我们可能需要上滑动窗口策略的方法:
经典题目:
Maximum Sum Subarray of Size K (easy)
Smallest Subarray with a given sum (easy)
Longest Substring with K Distinct Characters (medium)
Fruits into Baskets (medium)
No-repeat Substring (hard)
Longest Substring with Same Letters after Replacement (hard)
Longest Subarray with Ones after Replacement (hard)
双指针是这样的模式:两个指针朝着左右方向移动(双指针分为同向双指针和异向双指针),直到他们有一个或是两个都满足某种条件。双指针通常用在排好序的数组或是链表中寻找对子。比如,你需要去比较数组中每个元素和其他元素的关系时,你就需要用到双指针了。
我们需要双指针的原因是:如果你只用一个指针的话,你得来回跑才能在数组中找到你需要的答案。这一个指针来来回回的过程就很耗时和浪费空间了 — 这是考虑算法的复杂度分析的时候的重要概念。虽然brute force一个指针的解法可能会奏效,但时间复杂度一般会是O(n²)。在很多情况下,双指针能帮助我们找到空间或是时间复杂度更低的解。
识别使用双指针的招数:
经典题目:
Pair with Target Sum (easy)
Remove Duplicates (easy)
Squaring a Sorted Array (easy)
Triplet Sum to Zero (medium)
Triplet Sum Close to Target (medium)
Triplets with Smaller Sum (medium)
Subarrays with Product Less than a Target (medium)
Dutch National Flag Problem (medium)
这种模式,有一个非常出门的名字,叫龟兔赛跑。咱们肯定都知道龟兔赛跑啦。但还是再解释一下快慢指针:这种算法的两个指针的在数组上(或是链表上,序列上)的移动速度不一样。还别说,这种方法在解决有环的链表和数组时特别有用。
通过控制指针不同的移动速度(比如在环形链表上),这种算法证明了他们肯定会相遇的。快的一个指针肯定会追上慢的一个(可以想象成跑道上面跑得快的人套圈跑得慢的人)。
咋知道需要用快慢指针模式勒?
那啥时候用快慢指针而不是上面的双指针呢?
经典题目:
LinkedList Cycle (easy)
Start of LinkedList Cycle (medium)
Happy Number (medium)
Middle of the LinkedList (easy)
区间合并模式是一个用来处理有区间重叠的很高效的技术。在设计到区间的很多问题中,通常咱们需要要么判断是否有重叠,要么合并区间,如果他们重叠的话。这个模式是这么起作用的:
给两个区间,一个是a,另外一个是b。别小看就两个区间,他们之间的关系能跑出来6种情况。详细的就看图啦。
理解和识别这六种情况,灰常重要。因为这能帮你解决一大堆问题。这些问题从插入区间到优化区间合并都有。
怎么识别啥时候用合并区间模式呀?
经典题目:
Merge Intervals (medium)
Insert Interval (medium)
Intervals Intersection (medium)
Conflicting Appointments (medium)
这种模式讲述的是一直很好玩的方法:可以用来处理数组中的数值限定在一定的区间的问题。这种模式一个个遍历数组中的元素,如果当前这个数它不在其应该在的位置的话,咱们就把它和它应该在的那个位置上的数交换一下。你可以尝试将该数放到其正确的位置上,但这复杂度就会是O(n^2)。这样的话,可能就不是最优解了。因此循环排序的优势就体现出来了。
咋鉴别这种模式?
经典题目:
Cyclic Sort (easy)
Find the Missing Number (easy)
Find all Missing Numbers (easy)
Find the Duplicate Number (easy)
Find all Duplicate Numbers (easy)
在众多问题中,题目可能需要你去翻转链表中某一段的节点。通常,要求都是你得原地翻转,就是重复使用这些已经建好的节点,而不使用额外的空间。这个时候,原地翻转模式就要发挥威力了。
这种模式每次就翻转一个节点。一般需要用到多个变量,一个变量指向头结点(下图中的current),另外一个(previous)则指向咱们刚刚处理完的那个节点。在这种固定步长的方式下,你需要先将当前节点(current)指向前一个节点(previous),再移动到下一个。同时,你需要将previous总是更新到你刚刚新鲜处理完的节点,以保证正确性。
咱们怎么去甄别这种模式呢?
经典题目:
Reverse a LinkedList (easy)
Reverse a Sub-list (medium)
Reverse every K-element Sub-list (medium)
这种模式基于宽搜(Breadth First Search (BFS)),适用于需要遍历一颗树。借助于队列数据结构,从而能保证树的节点按照他们的层数打印出来。打印完当前层所有元素,才能执行到下一层。所有这种需要遍历树且需要一层一层遍历的问题,都能用这种模式高效解决。
这种树上的BFS模式是通过把根节点加到队列中,然后不断遍历直到队列为空。每一次循环中,我们都会把队头结点拿出来(remove),然后对其进行必要的操作。在删除每个节点的同时,其孩子节点,都会被加到队列中。
识别树上的BFS模式:
经典题目:
Binary Tree Level Order Traversal (easy)
Reverse Level Order Traversal (easy)
Zigzag Traversal (medium)
Level Averages in a Binary Tree (easy)
Minimum Depth of a Binary Tree (easy)
Level Order Successor (easy)
Connect Level Order Siblings (medium)
树形DFS基于深搜(Depth First Search (DFS))技术来实现树的遍历。
咱们可以用递归(或是显示栈,如果你想用迭代方式的话)来记录遍历过程中访问过的父节点。
该模式的运行方式是从根节点开始,如果该节点不是叶子节点,我们需要干三件事:
识别树形DFS:
经典题目:
Binary Tree Path Sum (easy)
All Paths for a Sum (medium)
Sum of Path Numbers (medium)
Path With Given Sequence (medium)
Count Paths for a Sum (medium)
很多问题中,我们被告知,我们拿到一大把可以分成两队的数字。为了解决这个问题,我们感兴趣的是,怎么把数字分成两半?使得:小的数字都放在一起,大的放在另外一半。双堆模式就能高效解决此类问题。
正如名字所示,该模式用到了两个堆,是不是很难猜?一个最小堆用来找最小元素;一个最大堆,拿到最大元素。这种模式将一半的元素放在最大堆中,这样你可以从这一堆中秒找到最大元素。同理,把剩下一半丢到最小堆中,O(1)时间找到他们中的最小元素。通过这样的方式,这一大堆元素的中位数就可以从两个堆的堆顶拿到数字,从而计算出来。
判断双堆模式的秘诀:
经典题目:
Find the Median of a Number Stream (medium)
Sliding Window Median (hard)
Maximize Capital (hard)
超级多的编程面试问题都会涉及到排列和组合问题。子集问题模式讲的是用BFS来处理这些问题。
这个模式是这样的:
给一组数字 [1, 5, 3]
该模式的详细步骤如下:
如果判断这种子集模式:
经典题目:
Subsets (easy)
Subsets With Duplicates (easy)
Permutations (medium)
String Permutations by changing case (medium)
Balanced Parentheses (hard)
Unique Generalized Abbreviations (hard)
当你需要解决的问题的输入是排好序的数组,链表,或是排好序的矩阵,要求咱们寻找某些特定元素。这个时候的不二选择就是二分搜索。这种模式是一种超级牛的用二分来解决问题的方式。
对于一组满足上升排列的数集来说,这种模式的步骤是这样的:
图示该过程的话,如下图所示:
经典题目:
Order-agnostic Binary Search (easy)
Ceiling of a Number (medium)
Next Letter (medium)
Number Range (medium)
Search in a Sorted Infinite Array (medium)
Minimum Difference Element (medium)
Bitonic Array Maximum (easy)
任何让我们求解最大/最小/最频繁的K个元素的题,都遵循这种模式。
用来记录这种前K类型的最佳数据结构就是堆了(译者注:在Java中,改了个名,叫优先队列(PriorityQueue))。这种模式借助堆来解决很多这种前K个数值的问题。
这个模式是这样的:
注意这种模式下,咱们不需要去排序数组,因为堆具有这种良好的局部有序性,这对咱们需要解决问题就够了。
识别最大K个元素模式:
经典题目:
Top ‘K’ Numbers (easy)
Kth Smallest Number (easy)
‘K’ Closest Points to the Origin (easy)
Connect Ropes (easy)
Top ‘K’ Frequent Numbers (medium)
Frequency Sort (medium)
Kth Largest Number in a Stream (medium)
‘K’ Closest Numbers (medium)
Maximum Distinct Elements (medium)
Sum of Elements (medium)
Rearrange String (hard)
K路归并能帮咱们解决那些涉及到多组排好序的数组的问题。
每当你的输入是K个排好序的数组,你就可以用堆来高效顺序遍历其中所有数组的所有元素。你可以将每个数组中最小的一个元素加入到最小堆中,从而得到全局最小值。当我们拿到这个全局最小值之后,再从该元素所在的数组里取出其后面紧挨着的元素,加入堆。如此往复直到处理完所有的元素。
该模式是这样的运行的:
识别K路归并:
经典题目:
Merge K Sorted Lists (medium)
Kth Smallest Number in M Sorted Lists (Medium)
Kth Smallest Number in a Sorted Matrix (Hard)
Smallest Number Range (Hard)
经典题目:
0/1 Knapsack (medium)
Equal Subset Sum Partition (medium)
Subset Sum (medium)
Minimum Subset Sum Difference (hard)
拓扑排序模式用来寻找一种线性的顺序,这些元素之间具有依懒性。比如,如果事件B依赖于事件A,那A在拓扑排序顺序中排在B的前面。
这种模式定义了一种简单方式来理解拓扑排序这种技术。
这种模式是这样奏效的:
拓扑排序模式识别:
经典题目:
Topological Sort (medium)
Tasks Scheduling (medium)
Tasks Scheduling Order (medium)
All Tasks Scheduling Orders (hard)
Alien Dictionary (hard)
大家好好练练这些题目,面试中遇到中高等难度的题目,应该就能解得不错了。
第二门则是单独将动态规划(DP)的题目进行了细分。
提到算法,绕不开的重点和难点就肯定会包括动态规划 -- DP,本文就把经典的DP问题按照分类列一下,大家可以按照Recursion,Top-Down,Bottom-Up三种方式都练一练。俗话说,熟能生巧,多练才是提高算法的不二法宝。
课程详细的内容,可以参考这里:
该门课程中, 作者将DP的问题分成以下几类:
0/1 Knapsack,0/1背包问题
Equal Subset Sum Partition,相等子集划分问题
Subset Sum,子集和问题
Minimum Subset Sum Difference,子集和的最小差问题
Count of Subset Sum,相等子集和的个数问题
Target Sum,寻找目标和的问题
Unbounded Knapsack,无限背包
Rod Cutting,切钢条问题
Coin Change,换硬币问题
Minimum Coin Change,凑齐每个数需要的最少硬币问题
Maximum Ribbon Cut,丝带的最大值切法
Fibonacci numbers,斐波那契数列问题
Staircase,爬楼梯问题
Number factors,分解因子问题
Minimum jumps to reach the end,蛙跳最小步数问题
Minimum jumps with fee,蛙跳带有代价的问题
House thief,偷房子问题
Longest Palindromic Subsequence,最长回文子序列
Longest Palindromic Substring,最长回文子字符串
Count of Palindromic Substrings,最长子字符串的个数问题
Minimum Deletions in a String to make it a Palindrome,怎么删掉最少字符构成回文
Palindromic Partitioning,怎么分配字符,形成回文
Longest Common Substring,最长相同子串
Longest Common Subsequence,最长相同子序列
Minimum Deletions & Insertions to Transform a String into another,字符串变换
Longest Increasing Subsequence,最长上升子序列
Maximum Sum Increasing Subsequence,最长上升子序列和
Shortest Common Super-sequence,最短超级子序列
Minimum Deletions to Make a Sequence Sorted,最少删除变换出子序列
Longest Repeating Subsequence,最长重复子序列
Subsequence Pattern Matching,子序列匹配
Longest Bitonic Subsequence,最长字节子序列
Longest Alternating Subsequence,最长交差变换子序列
Edit Distance,编辑距离
Strings Interleaving,交织字符串
大家可以先把以上35个题目练熟,这样DP到达中等水平肯定是okay了的。再加以训练和提高。突破算法的硬骨头不在话下。一定要按照三种方式对照起来练。
动态规划还被单独拿出来细分了,对DP比较吃力的,可以跟着这个分类好好刷。每个题目按照递归,Top-Down,Bottom-Up都来一遍,虽然只有35题左右,认真吸收,入门不在话下。
以上。