一、问题
1、写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
2、示例
输入:n = 2
输出:1
输入:n = 5
输出:5
二、解题方法
解题思路:
斐波那契数列的定义是 f(n + 1) = f(n) + f(n - 1)f(n+1)=f(n)+f(n−1) ,生成第 nn 项的做法有以下几种:
1、递归法:
原理: 把 f(n)f(n) 问题的计算拆分成 f(n-1)f(n−1) 和 f(n-2)f(n−2) 两个子问题的计算,并递归,以 f(0)f(0) 和 f(1)f(1) 为终止条件。
缺点: 大量重复的递归计算,例如 f(n)f(n) 和 f(n - 1)f(n−1) 两者向下递归需要 各自计算 f(n - 2)f(n−2) 的值。
2、记忆化递归法:
原理: 在递归法的基础上,新建一个长度为 nn 的数组,用于在递归时存储 f(0)f(0) 至 f(n)f(n) 的数字值,重复遇到某数字则直接从数组取用,避免了重复的递归计算。
缺点: 记忆化存储需要使用 O(N)O(N) 的额外空间。
3、动态规划:
原理: 以斐波那契数列性质 f(n + 1) = f(n) + f(n - 1)f(n+1)=f(n)+f(n−1) 为转移方程。
从计算效率、空间复杂度上看,动态规划是本题的最佳解法。
package com.haoxiansheng.demo01.SwordfingerOffer;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
/**
* @author flame
* @data 2020/10/22
*/
@Slf4j
public class FibDemo {
public static void main(String[] args) {
log.info("fib=>{}", fib(2000));
}
// 为什么选1000000007
//理由大概是“int32类型是十位数,对1e9取模可防止int32溢出”、“1e9+7是质数,
//对质数取模可以尽可能地让模数避免相等”以及“1e9+7是离1e9最近的质数,比较好记”
public static int fib(int n) {
int a =0;
int b = 1;
int sum;
for (int i =0 ; i< n; i++) {
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
int constant = 1000000007;
public int fib1(int n) {
if (n < 2)
return n;
int first = fib(n - 1) % constant;
int second = fib(n - 2) % constant;
return (first + second) % constant;
}
public int fib2(int n) {
return fib(n, new HashMap<>());
}
public int fib(int n, Map<Integer, Integer> map) {
if (n < 2)
return n;
if (map.containsKey(n))
return map.get(n);
int first = fib(n - 1, map) % constant;
map.put(n - 1, first);
int second = fib(n - 2, map) % constant;
map.put(n - 2, second);
int res = (first + second) % constant;
map.put(n, res);
return res;
}
// 非递归解决
public int fib3(int n) {
int constant = 1000000007;
int first = 0;
int second = 1;
while (n-- > 0) {
int temp = first + second;
first = second % constant;
second = temp % constant;
}
return first;
}
}
1000000007 需要研究一下。计算需要画流程区理解