英文:Dynamic Programming,简称DP,将问题分解为互相重叠的子问题,通过反复求解子问题来解决原问题就是动态规划,如果某一问题有很多重叠子问题,使用动态规划来解是比较有效的。
求解动态规划的核心问题是穷举
经典问题:背包问题,打家劫舍,股票问题,子序列问题
题目:1 1 2 3 5 8 求斐波那契数列
// 动规五部曲
// 1. 确定dp数组(dp table)以及下标的含义
dp[i]:第i个斐波那契数值为dp[i]
// 2. 确定递推公式
dp[i] = dp[i - 1] + dp[i - 2]
// 3. dp数组如何初始化
dp[0] = 1 dp[i] = 1
// 4. 确定遍历顺序
从前向后
// 5. 打印dp数组
let dp = []
dp[0] = 0 dp[1] = 1
for(i = 2; i < n; i ++)
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
股票问题
kmp算法
题目:文本串 aabaabaaf, 模式串 aabaaf, 求是否存在匹配模式串
根据kmp算法需要求next[](prefix) ,及最长相等前后缀的数组,next值,代表下次开始匹配的位置下标。首先初始 j (前缀末尾 )i (后缀末尾)next[],然后判断前后缀不相同时条件j > 0 && s[i] !== s[j]时,j赋值为next数组前面一位的值,判断前后缀相同时j 向前移动一位,最后给next赋值
getNext(s) {
const j = 0;
const next = []
const next[0] = j
for (let i = 1; i < s.length; i++) {
while(j > 0 && s[i] !== s[j]) {
j = next[j - 1]
}
if (s[i] === s[j]) {
j++
}
next[i] = j
}
}
let eg = aabaabaaf
let template = aabaaf
// next [0 1 0 1 2 0]
// 最长相等前后缀
// a 0
// aa 1
// aab 0
// aaba 1
// aabaa 2
// aabaaf 0
// 前缀 a aa aab aaba aabaa
// 后缀 f af aaf baaf abaaf
回溯算法
经典问题:解决一组数的组合,切割,子集,排列,棋盘问题
确认递归函数参数,返回值,设置终止条件,编写单层递归逻辑代码
void backtracking(参数) {
if (终止条件)
收集结果
return
for(集合元素)
处理节点
递归函数
回溯操作
}
题目:从0-n的数,数组长度为k长度的组合,求所有组合
// 二维数组 result
// 一维数组 path
const path = []
const backtracking = (n, k, startIndex) => {
if (path.length === k) {
console.log(path)
return
}
for (let i = startIndex; i < n.length - (k - path.length) + 1; i++) {
path.push(n[i])
backtracking(n, k, i + 1)
path.pop()
}
}
backtracking([1, 2, 3, 4], 2, 0)
// [ 1, 2 ]
// [ 1, 3 ]
// [ 1, 4 ]
// [ 2, 3 ]
// [ 2, 4 ]
// [ 3, 4 ]
// for 代表的是单前层的遍历,递归是往树的深处遍历,剪枝一般在for循环里做处理
// 比如i < n.size => i < n - (k - path.size) + 1 ()
\