求斐波拉契数
斐波拉契数为,Fib(N) = Fib(N-1)+Fib(N-2) F(0)=F(1)=1 用Java编写能求Fib(N)的程序 输入为N,须输出Fib(N)
如输入:
3
输出:
3
注意:问题比较容易出在sn的类型上面,用long一般就没问题,用double或者int可能会在某些测试平台上无法得到满分
public class Fibonacci { public static void main(String[] args) { // 1、暴力递归 long start1 = System.currentTimeMillis(); System.out.println(fib(40)); long end1 = System.currentTimeMillis(); System.out.println("暴力递归时间:" + (end1 - start1)); // 2、备忘录 long start2 = System.currentTimeMillis(); System.out.println(fibHelper(40)); long end2 = System.currentTimeMillis(); System.out.println("备忘录时间:" + (end2 - start2)); // 3、dpTable long start3 = System.currentTimeMillis(); System.out.println(dpTable(40)); long end3 = System.currentTimeMillis(); System.out.println("备忘录时间:" + (end3 - start3)); // 4、lessSpace long start4 = System.currentTimeMillis(); System.out.println(lessSpace(40)); long end4 = System.currentTimeMillis(); System.out.println("备忘录时间:" + (end4 - start4)); } // 暴力递归 public static double fib(int n) { if (n == 0) return 0; if (n == 1 || n == 2) return 1; return fib(n - 1) + fib(n - 2); } // 备忘录 public static double fibHelper(int n) { if (n == 0) return 0; double[] memo = new double[n + 1]; return helper(memo, n); } public static double helper(double[] memo, int n) { if (n == 1 || n == 2) return 1; if (memo[n] != 0) return memo[n]; memo[n] = helper(memo, n - 1) + helper(memo, n - 2); return memo[n]; } // DP table public static double dpTable(int n) { if (n == 0) return 0; if (n == 1 || n == 2) return 1; double[] dp = new double[n + 1]; dp[1] = dp[2] = 1; for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } // 优化存储空间方案 public static double lessSpace(int n) { if (n == 0) return 0; if (n == 1 || n == 2) return 1; double before = 1, total = 1; for (int i = 3; i <= n; i++) { double sum = before + total; before = total; total = sum; } return total; } }
计算结果:
1.02334155E8
暴力递归时间:353
1.02334155E8
备忘录时间:0
1.02334155E8
DPtable时间:0
1.02334155E8
优化空间时间:0
进阶:
上面的暴力递归,时间复杂度=O(2^n),空间复杂度=O(1),可以清楚得看见时间上暴力递归比另外三种的差距,如果你的计算数据再大一点,暴力递归将会花费更多的时间。
如果我们使用备忘录或者dp table,就可以将时间复杂度降低到O(n),但是空间复杂度将变为O(n),从时间复杂度上面,已经可以算得上是降维打击了,毕竟CPU可比内存贵多了。
这两者的区别是前者是自顶向上,后者是自底向上计算。
把备忘录独立出来,就成了dp table。dp table省掉了一个递归,从代码上面来说更加简洁。
第四种方法,我们不难从dp table可以看出,我们只需要记录当前数值,和前面两个数值就可以了,并不需要把所有的数据都记录下来。肉眼可见得我们又将空间复杂度变为了O(1),时间复杂度依然是O(n),岂不美哉。