首先这是传统递归
// 传统方法
public Long fib(int n) {
if (n == 0) {
return 0L;
} else if (n == 1 || n == 2) {
return 1L;
} else {
return fib(n - 1) + fib(n - 2);
}
}
备忘录算法 这里用的是map字典
// 备忘录
public Long backupFib(int n) {
if (n < 1) {
return 0L;
}
// 列,列项和
Map<Integer, Long> map = new HashMap<>(16);
return this.helper(map,n);
}
public Long helper(Map<Integer, Long> map, int n) {
if (n == 1 || n == 2) {
return 1L;
}
if (map.containsKey(n)) {
return map.get(n);
}
map.put(n,helper(map,n-1)+helper(map,n-2));
return map.get(n);
}
dp数组迭代解法
// dp数组迭代
public Long dpFib(int n) {
Long[] dp = new Long[]{
};
dp[0] = 0L;
dp[1] = dp[2] = 1L;
for (int i = 3; i <= n ; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
// dp优化
public Long dpUpFib(int n) {
if (n == 0) {
return 0L;
} else if (n == 1 || n == 2) {
return 1L;
}
Long prev = 1L;
Long curr = 1L;
for (int i = 3; i <= n ; i++) {
Long sum = prev+curr;
prev = curr;
curr = sum;
}
return curr;
}
可以看到 在40项的时候 传统递归劣势已经体现,而在45项往后 传统递归会几乎指数型降低 非常慢!(不做截图展示)
当项数到达一千时 (传统递归已经完全不适用 注释掉)可以看到两者效率都很高 dp略大于map
解法思路:
1、递归没什么好说的 又让人头大 效率又低
2、备忘录算法: 把n项的下标当成map的key 把n项值当成map的value ,
例如数列:1、1、2、3、5 、8。。。。 第4项 map的key就是4 ,value是 3 ,第5项key value都是5
计算第6项时,我们只需将key为 4 和key 为5 两项的value相加即可
3、dp算法: 和备忘录很相似 只不过备忘录是⾃顶向下 就是说 计算第6项 是去找第4,5项,而dp则是⾃底向上,
这个 自底向上
自底向下 可能不是很好理解,两种算法乍一看也差不多 但是看到dp优化的时候 就会比较明朗