-
普通递归(自顶向下)
求解F(n),先求解F(n-1)+F(n-2),大量重复计算
时间复杂度:O(2^n)
空间复杂度:O(n)
int fib(int n){ if(n == 0){ return 0; } if(n == 1){ return 1; } //避免溢出,返回的结果都模1000000007 return fib(n-1) % M + fib(n-2) % M; }
-
尾递归(自底向上)
尾递归的一个特点是在回归过程中不用做任何操作,当编译器检测到一个函数使用尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建新的,这样栈空间大大缩减,从而提升运行效率。
并且利用了每次递归调用时,都会把之前已经计算好的结果以参数的形式传递过去,避免重复计算
时间复杂度:O(n)
空间复杂度:O(n)
int fib_1(int n, int a, int b){ //a是第n项的值,b是第n+1项的值 if(n == 0){ return 0; } if(n == 1){ //从F(1)、F(2)开始 return a; } return fib_1(n-1,b % M,(a+b) % M); //调用时的a,b的初值是1 1 }
-
记忆化搜索(自顶向下)
依然是自顶向下的方法,额外开辟一个数组用于保存的计算过的值,使用递归实现,但是避免了重复计算,已经存在存在的值直接返回,计算的都是需要的值
时间复杂度:O(n)
空间复杂度: O(n)
int fib_2(int n){ if(n == 0){ return 0; } if(n == 1 || n == 2){ return 1; } if(memory[n] == 0){ //已经计算过的直接返回 memory[n] = fib_2(n-1) % M + fib_2(n-2) % M; } return memory[n]; }
-
动态规划
初始状态: dp[0] = 0 dp[1] = 1, 定义状态转移方程:dp[n] = dp(n-1) + dp[n-2]
时间复杂度:O(n)
空间复杂度:O(n)
int fib_3(int n){ if(n < 0){ return -1; } memory[0] = 0; memory[1] = 1; for(int i = 2;i <= n;i++){ memory[i] = memory[i-1] % M + memory[i-2] % M; } return memory[n]; }
-
动态规划的空间复杂度优化
使用三个变量交替前进,代替了使用一个dp数组,将空间复杂度降到O(1),时间复杂度还是O(n)
int fib_4(int n) { if(n == 0){ return 0; } int temp,a1=1,a2=1; //从n==1开始计算 for(int i = 1;i < n;i++){ temp = a2; a2 = (a1+a2) % M; //加的是下一次的结果 a1 = temp; //返回的是上次加的结果 } return a1; }