前言
机智的摸鱼人决不用功要反复刷leetcode,他只巧妙地偷懒。
面试常见算法题绝对不超过一百种,只需每种掌握一两道题(必刷题),总结适用场景、解题模板,便可以照葫芦画瓢做其余大部分题目。
面对没刷过的题目,如果一时没有头绪,就花半个小时思考该题与必刷题的联系,如果思考不出来,实在是意味着你对必刷题的理解不够深入,亦或者意味着你的智商不适合摸鱼。
由于研二前应该没有机试,本文的必刷题尚未涵盖各种题型,已经涵盖的题型包括:两数之和问题/哈希法、DFS/回溯、BFS、树、动态规划(子序列问题、编辑距离、股票问题、打家劫舍问题、高楼扔鸡蛋问题)、区间问题(区间调度问题/贪心算法、区间合并问题、区间交集问题)调度问题)、二分查找、双指针问题、滑动窗口问题/子字符串匹配问题(最小覆盖子串、易位词、最大无重复子串)、排序预处理(信封嵌套问题)、前缀和问题、区间交集问题,剩下的内容笔者计划两年内更新完。
读者应该按照上面的指导思想,自行构建必刷题库,并在实践中不断更新题库,面试时,对自己的题库进行检索即可。
笔者前前后后刷了四次leetcode,自认为这是最节省时间的刷题方法,非常适合懒惰的摸鱼人以及小镇做题家。
注:与其它教程相比,本教程的特点在于比较适合聪明但懒惰的同学;如果读者智商低于程序员的平均水平,更适合采用其它刷题教程的方法。
2020.12.7
两数之和问题/哈希法
核心是哈希表的创建
![efc510d87ae17e3230fc1e0fe169de3c.png](https://img-blog.csdnimg.cn/img_convert/efc510d87ae17e3230fc1e0fe169de3c.png)
(1)值得注意的是,查找的时间复杂度是O(N),如果是大量查找,会非常耗时,解决方法是创建一个哈希表存储所有组合的和
(2)还可以先排序,再用双指针解决问题
(3)如果数组是有序的,最好使用双指针
回溯法/DFS
核心是五步
多用于:
排列组合
求海岛面积
![88f264a60550115b0c0755cfd29c5d9a.png](https://img-blog.csdnimg.cn/img_convert/88f264a60550115b0c0755cfd29c5d9a.png)
BFS
多用于求最短距离
3+3步(创建数据结构、数据结构的初始化、记录更新步数+遍历队列、检测终点、更新数据结构)
![a388c4b0cd3902cca4985c543fe3b4dc.png](https://img-blog.csdnimg.cn/img_convert/a388c4b0cd3902cca4985c543fe3b4dc.png)
树
- 前序遍历
树的某些性质决定了要用前序遍历
![95d45bd430186e9292a909e806cf8874.png](https://img-blog.csdnimg.cn/img_convert/95d45bd430186e9292a909e806cf8874.png)
2. 后序遍历
多用于root+left+right
![9f7b9e4317b53d4343e630aa11cb6439.png](https://img-blog.csdnimg.cn/img_convert/9f7b9e4317b53d4343e630aa11cb6439.png)
3. 中序遍历
多用于二叉搜索树
![f1edccd400dd367ed3c901581ff51217.png](https://img-blog.csdnimg.cn/img_convert/f1edccd400dd367ed3c901581ff51217.png)
4. N叉树的遍历
回溯法的本质就是n叉树的遍历
![4724be178f4a3d2519556cedb0f39dbc.png](https://img-blog.csdnimg.cn/img_convert/4724be178f4a3d2519556cedb0f39dbc.png)
动态规划
穷举三要素:重叠子问题(暴力枚举有重复计算现象)、最优子结构(结果之间有关系)、状态转移方程
思考状态转移方程:
(1)明确 base case -> 明确「状态」-> 明确「选择」 -> 定义 dp 数组/函数的含义。(2)base case与状态往往是同一个元素,选择是导致状态变化的行为,dp函数的输入为状态、输出为题目要求计算的量、内部核心是做选择,选择包括主动选择和被动选择。
核心:自底向上,思考重叠子问题的消除方法
- 最最简单的动态规划——零钱兑换问题
![4a32bf7e4581cdfb831399381791d75f.png](https://img-blog.csdnimg.cn/img_convert/4a32bf7e4581cdfb831399381791d75f.png)
![1c5e13c8aaac65b6eee3b9114bf61d9c.png](https://img-blog.csdnimg.cn/img_convert/1c5e13c8aaac65b6eee3b9114bf61d9c.png)
2. 子序列问题
非常有代表性,dp数组无非就是那几种形式
(1)最长递增子序列
在子数组array[0..i]中,我们要求的子序列(最长递增子序列)的长度是dp[i]
状态i的含义是前i个元素的最长上升子序列,必须包含第i个元素
base case是只有一个元素的序列
![5b4ec6144f9e6c9d5f42a65611fe16e0.png](https://img-blog.csdnimg.cn/img_convert/5b4ec6144f9e6c9d5f42a65611fe16e0.png)
(2)最长公共子序列
涉及两个字符串/数组时(比如最长公共子序列),dp 数组的含义如下:在子数组 arr1[0..i]
和子数组 arr2[0..j]
中,我们要求的子序列(最长公共子序列)长度为 dp[i][j]
二维数组,脑子里面要有两个指针
状态i,j是指针的位置
base case是字符串长度为0
![0dec02c48eeb856ca6bf7ce085ed4419.png](https://img-blog.csdnimg.cn/img_convert/0dec02c48eeb856ca6bf7ce085ed4419.png)
(3)最长回文子序列
只涉及一个字符串/数组时(比如本文要讲的最长回文子序列),dp 数组的含义如下:
在子数组 array[i..j]
中,我们要求的子序列(最长回文子序列)的长度为 dp[i][j]
。
状态i,j的含义是从i到j的最大回文子序列
base case:i=j时,当个字母组成长度为1的回文子序列
一定要根据状态转移方程、base case、目标值,思考遍历方向
![6006382047d99a94248b029479c493d8.png](https://img-blog.csdnimg.cn/img_convert/6006382047d99a94248b029479c493d8.png)
3. 编辑距离问题
先思考递归方法,然后把递归改成动态规划
![55067231f8e70d18f25a4c9ac0e44975.png](https://img-blog.csdnimg.cn/img_convert/55067231f8e70d18f25a4c9ac0e44975.png)
4. 股票问题
![e9dad44897398f9bde8aa123d7dc40e6.png](https://img-blog.csdnimg.cn/img_convert/e9dad44897398f9bde8aa123d7dc40e6.png)
![39358bc1e05f5f80838e2902f6cca8d2.png](https://img-blog.csdnimg.cn/img_convert/39358bc1e05f5f80838e2902f6cca8d2.png)
![5d73eedd4505afe9ba0ba70f533f7980.png](https://img-blog.csdnimg.cn/img_convert/5d73eedd4505afe9ba0ba70f533f7980.png)
![34f1986b309179c089ff2177e0d55f37.png](https://img-blog.csdnimg.cn/img_convert/34f1986b309179c089ff2177e0d55f37.png)
5. 打家劫舍问题
根据状态转移方程倒序遍历
状态的含义是小偷的出发点
![b6515581957e58710a5d5e0dc2b86b79.png](https://img-blog.csdnimg.cn/img_convert/b6515581957e58710a5d5e0dc2b86b79.png)
6. 高楼扔鸡蛋问题
这是一个复杂的题目,表现在哪怕手算也不会很快出思路
最坏情况发生在搜索空间穷尽时
一定要想明白状态的含义,一定要在纸上写一下状态转移方程
![ed71c46a3a78565de0ad9367053e23d1.png](https://img-blog.csdnimg.cn/img_convert/ed71c46a3a78565de0ad9367053e23d1.png)
区间问题
区间问题大多先排序
- 区间调度问题/贪心算法
![2c06f973db5624a8e4f637143572bf69.png](https://img-blog.csdnimg.cn/img_convert/2c06f973db5624a8e4f637143572bf69.png)
2. 区间合并问题
![f629c85093ed0f51ada16e9ecd8700d0.png](https://img-blog.csdnimg.cn/img_convert/f629c85093ed0f51ada16e9ecd8700d0.png)
3. 区间交集问题
![4e8060ba06f134fccebb1d39c0efb803.png](https://img-blog.csdnimg.cn/img_convert/4e8060ba06f134fccebb1d39c0efb803.png)
二分查找
时刻思考搜索区间的形状即可
- 查找特定元素
![392bab105c425fc31273c31a2943235f.png](https://img-blog.csdnimg.cn/img_convert/392bab105c425fc31273c31a2943235f.png)
2. 查找左右边界
![e6a09026ad0236667aee643e34e15f93.png](https://img-blog.csdnimg.cn/img_convert/e6a09026ad0236667aee643e34e15f93.png)
双指针问题
指针多用于查找
- 快慢指针
多用于判断链表有无环、寻找链表中点、寻找链表倒数第K个元素
2. 左右指针
多用于二分查找、有序数组的两数之和问题、反转数组、滑动窗口问题(子字符串匹配问题)
滑动窗口问题
- 最小覆盖子串问题
框架:先是窗口的right端右移,找到目标;再是窗口的left端右移,优化结果;循环遍历整个字符串
声明并定义——左右指针、字典、match、结果
while (right < s.size()) {
判断字典是否需要更新,window.add(s[right]),判断match是否需要更新;
right++;
while (valid) {
判断字典是否需要更新,window.remove(s[left]),判断match是否需要更新;
left++;
}
}
![80416f1c90632f3072a18a372cf75f31.png](https://img-blog.csdnimg.cn/img_convert/80416f1c90632f3072a18a372cf75f31.png)
(1)要用两个字典来记录
(2)字典、match的更新很重要
(3)对结果更新的判断语句很重要
(4)别忘了判断指针所指元素是否在目标字符串中
2. 找字母易位词
![b6143759092051d17c0020d14f244f3c.png](https://img-blog.csdnimg.cn/img_convert/b6143759092051d17c0020d14f244f3c.png)
2、3的架构与1相同,一定一定要熟悉这个架构,3更简单一些,字典索引不要弄错
3. 找无重复字符的最长子串
![e6ffb9cd9842185896262d0c00a2dd47.png](https://img-blog.csdnimg.cn/img_convert/e6ffb9cd9842185896262d0c00a2dd47.png)
信封嵌套问题
![5904b6701bc6dae8e42d7820e549b752.png](https://img-blog.csdnimg.cn/img_convert/5904b6701bc6dae8e42d7820e549b752.png)
(1)很多算法问题都需要排序技巧,其难点不在于排序本身,而是需要巧妙地排序进行预处理,将算法问题进行转换,为之后的操作打下基础。
(2)先按照宽度升序排列,宽度相同的按照长度降序排列,再按照宽度找最长递增子序列。
前缀和问题
- 如果有大量“求区间元素和”的操作,要先求前缀和
- 使用哈希法可免于二层嵌套
![c100cca9ecafddfbab904cf05338b203.png](https://img-blog.csdnimg.cn/img_convert/c100cca9ecafddfbab904cf05338b203.png)