Fibonaci数列,求f(n)?
# 方法一:
int fib(int n){
// base case
if(n==0||n==1) return n;
return fib(n-1) + fib(n-2);
}
# 这种方法虽然简单,但是时间复杂度却为指数级的,而且存在很多重复子问题。
# 那么如何消除这种重复子问题呢?比如f(17)是计算了两次(如图1),很显然每次计算都要
# 该f(n)是否遍历过呢,定义一个备忘录数组memo[n+1],初始化为0.
# 方法二:
int fib(int n){
// 备忘录全部初始化为0
int[] memo = new int[n+1]; #这里由于fib(n)对应着 memo[n],数组从0开始,所以申请n+1个大小长度
return helper(memo,n);
}
int helper(int[] memo,int n){
// base case
if(n==0||n==1) return n;
if(memo[n]!=0) return memo[n];
memo[n] = helper(memo,n-1) + helper(memo,n-2);
return memo[n];
}
# 采用备忘录后直接就把树形结构的指数级时间复杂度,转化为链式的o(n)。如图2
# 是否还能继续优化呢?我们看到既然通过备忘录把二叉树拉成了链式结构,那么空间复杂度有必要用o(n+1)个吗?很显然从图2中看出,只需要存储前面的两个就能得到当前的元素。
# 方法三:
int fib(int n){
// base case
if(n==0||n==1) return n;
int dp_i_1 = 1;
int dp_i_2 = 0;
for(int i = 2;i<=n;i++){
int dp_i = dp_i_1 + dp_i_2 ;
# 滚动交换
dp_i_2 = dp_i_1 ;
dp_i_1 = dp_i ;
}
return dp_i_1 ;
}
图1
图2