比如困扰我很久的二分法:

再比如考察频率贼高的深度优先搜索(DFS):

很多大佬看完会diss我:这些代码有啥NB的,我也能写出来,但问题是它真的好用啊,可以说是算法面试的刷题利器!
这份算法模板是我在《九章算法班》的首节免费试听讲座上拿到的,主讲人令狐冲分享了很多算法面试的技巧,感兴趣的话你们也可以去免费试听下~
接下来,具体说下这份模板怎么用,又是怎么让我感到“醍醐灌顶”的。
排序算法
排序算法一般是考察其中的快速排序和归并排序及相关的题,必须背诵这两个排序。
先来看这道例题,大家可以试着做一下:
LintCode 463. 整数排序
如果做不出来,就可以参考这个模板。
复杂度
-
时间复杂度:
-
快速排序(期望复杂度) : O(nlogn)
-
归并排序(最坏复杂度) : O(nlogn)
-
空间复杂度:
-
快速排序 : O(1)
-
归并排序 : O(n)
代码模板

接下来,试着套用模板做这道题:
LintCode 464. 整数排序 II
能做出来你也就出师了,接下来就是通过反复练习+理解思路不断巩固,我现在在面试中遇到排序算法基本可以直接秒。

宽度优先搜索 BFS
再说说DFS的好兄弟BFS。宽度优先搜索的考察频率高,实现一般都不难,在准备算法面试中可以优先考虑搞定它。
依葫芦画瓢一下,会使用DFS来解题的情况包括:
使用条件
- 拓扑排序(100%)
- 出现连通块的关键词(100%)
- 分层遍历(100%)
- 简单图最短路径(100%)
- 给定一个变换规则,从初始状态变到终止状态最少几步(100%)
复杂度
- 时间复杂度:O(n + m)
- n 是点数, m 是边数
- 空间复杂度:O(n)
代码模板

接下来,结合这几道例题来理解这份模板:
动态规划
再来说个特别的,最令人头大的动态规划。
令狐老师在《FB面试官揭露算法技巧》的讲座中有提到动态规划的解题法则
首先要搞懂动态规划的使用场景
这些情况不适用动规,不要被坑:
- 找所有具体的方案(准确率99%)
- 输入数据无序(除了背包问题外,准确率60%~70%)
- 暴力算法已经是多项式时间复杂度(准确率80%)
这些情景可以考虑使用动态规划,但也别钻牛角尖:
- 求方案总数(90%)
- 求最值(80%)
- 求可行性(80%)
接下来,搞懂第一个重要概念:动态规划四要素(对比递归的四要素):
- 状态 (State) – 递归的定义
- 方程 (Function) – 递归的拆解
- 初始化 (Initialization) – 递归的出口
- 答案 (Answer) – 递归的调用
现在,我们就可以根据动态规划的不同类型找到不同解法:
①背包型
- 给出 n 个物品及其大小,问是否能挑选出一些物品装满大小为m的背包
- 题目中通常有“和”与“差”的概念,数值会被放到状态中
- 通常是二维的状态数组,前 i 个组成和为 j 状态数组的大小需要开 (n + 1) * (m + 1)
对应解法
a.背包
- 状态 state
dp[i][j] 表示前 i 个数里挑若干个数是否能组成和为 j
方程 function
dp[i][j] = dp[i - 1][j] or dp[i - 1][j - A[i - 1]] 如果 j >= A[i - 1]
dp[i][j] = dp[i - 1][j] 如果 j < A[i - 1]
第 i 个数的下标是 i - 1,所以用的是 A[i - 1] 而不是 A[i]
初始化 initialization
dp[0][0] = true
dp[0][1...m] = false
答案 answer
使得 dp[n][v], 0 s <= v <= m 为 true 的最大 v
b.多重背包
- 状态 state
dp[i][j] 表示前i个物品挑出一些放到 j 的背包里的最大价值和
方程 function
dp[i][j] = max(dp[i - 1][j - count * A[i - 1]] + count * V[i - 1])
其中 0 <= count <= j / A[i - 1]
初始化 initialization
dp[0][0..m] = 0
答案 answer
dp[n][m]
②区间型
- 题目中有 subarray / substring 的信息
- 大区间依赖小区间
用dp[i][j]表示数组/字符串中i, j这一段区间的最优值/可行性/方案总数 - 状态 state
dp[i][j] 表示数组/字符串中 i,j 这一段区间的最优值/可行性/方案总数
方程 function
dp[i][j] = max/min/sum/or(dp[i,j 之内更小的若干区间])
③匹配型
- 通常给出两个字符串
- 两个字符串的匹配值依赖于两个字符串前缀的匹配值
- 字符串长度为 n,m 则需要开 (n + 1) x (m + 1) 的状态数组
- 要初始化
dp[i][0]与dp[0][i] - 通常都可以用滚动数组进行空间优化
- 状态 state
dp[i][j] 表示第一个字符串的前 i 个字符与第二个字符串的前 j 个字符怎么样怎么样(max/min/sum/or)
④划分型
- 是前缀型动态规划的一种, 有前缀的思想
- 如果指定了要划分为几个部分:
dp[i][j]表示前i个数/字符划分为j个 部分的最优值/方案数/可行性- 如果没有指定划分为几个部分:
dp[i]表示前i个数/字符划分为若干个 部分的最优值/方案数/可行性- 状态 state
指定了要划分为几个部分:dp[i][j]表示前i个数/字符划分为j个部分的最优值/方案数/可行性
没有指定划分为几个部分:dp[i]表示前i个数/字符划分为若干个部分的最优值/方案数/可行性
⑤接龙型
- 通常会给一个接龙规则,问你最长的龙有多长
状态表示通常为: dp[i] 表示以坐标为 i 的元素结尾的最长龙的长度- 方程通常是:
dp[i] = max{dp[j] + 1}, j 的后面可以接上 i - LIS 的二分做法选择性的掌握,但并不是所有的接龙型DP都可以用二分来优化
- 状态 state
状态表示通常为:dp[i] 表示以坐标为 i 的元素结尾的最长龙的长度
方程 function
dp[i] = max{dp[j] + 1}, j 的后面可以接上 i
复杂度
- 时间复杂度:
- O(状态总数 * 每个状态的处理耗费)
- 等于O(状态总数 * 决策数)
- 空间复杂度:
- O(状态总数) (不使用滚动数组优化)
- O(状态总数 / n)(使用滚动数组优化, n是被滚动掉的那一个维度)
可以来试试这几道领扣例题
差不多就分享到这里啦,像是双指针、排序算法、二叉树、宽度优先搜索、深度优先搜索的使用条件、时间复杂度、代码模板,这份模板也都有涵盖。

当然啦,学习算法还是要一步步循序渐进,在理解解题思路的情况下去背诵这份模板,这样它才能真正的发挥价值。
本文分享了排序算法(快速/归并排序)、深度优先搜索(DFS)和宽度优先搜索(BFS)的模板,以及动态规划的解题策略。通过实例演示如何运用模板解答LintCode题目,助你高效解决面试难题。
9万+

被折叠的 条评论
为什么被折叠?



