斐波那契数列面试题解法(java)
来源:互联网 发布:数据库blob什么意思 编辑:程序博客网 时间:2024/06/05 06:47
斐波那契数列经常出现在程序员面试的题目中,本文总结一下斐波那契数列的解法
斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)
题目1:给定整数N,代表台阶数,小明一次可以上一个台阶或一次上两个台阶(因为怕扯着蛋,一次最多上两个台阶),问上N节台阶总共有多少种方法?
问题分析:记f(n)表示上n节台阶的所有方法总数。小明初始站在第0级台阶上,f(0)=1;上第一节台阶,只能一次上去,f(1) = 1;小明上楼梯时要么经过第一级台阶,要么越过第一级台阶(越过第一级台阶只能站第二级台阶上),所以经过一步之后问题变成f(n) = f(n-1) + f(n-2)。f(n-1)表示站在第一级台阶上还剩多少种走法,f(n-2)表示站在第二级台阶上还是多少种走法。
题目2:假设农场中成熟的母牛每年只会生1头小母牛,并且永远不会死。第一年农场有1只成熟的母牛,从第二年开始,母牛开始生小母牛。给定整数N,求出N年后牛的数量。
题目3:我们有一个2xN的长条形棋盘,然后用1x2的骨牌去覆盖整个棋盘。对于这个棋盘,一共有多少种不同的覆盖方法呢?
详见http://hihocoder.com/problemset/problem/1143
计算结果一般很大,有时面试时需要使用BigInteger来表示。有时也会让结果mod一个数输出,这里假设mod19999997作为结果输出。
首先根据定义轻松写出暴力递归的代码
import java.util.Scanner;class Fibonacci{ //int能表示的最大值的一半,两个小于halfMax的值相加结果不会越界 private static int halfMax = Integer.MAX_VALUE>>1; //求斐波那契数列第n项,结果对m求余 public static int fibonacci(int n,int m) { if(n<1) return 0; if(n==1 || n==2) return 1; if(m<=halfMax){ int result = fibonacci(n-1,m) + fibonacci(n-2,m); //return result%m; //本来应该进行求余操作,由于result不会大于2m,这里用减法速度回更快 return result>=m?result-m:result; } else{ long result = (long)fibonacci(n-1,m) + fibonacci(n-2,m); return (int) (result>=m?result-m:result); } }}public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); System.out.println(Fibonacci.fibonacci(n, 19999997)); }}
面试的时候写成这样肯定是不合格的,这样写的时间复杂度为2的n次方,计算30以内的还可以,更大的数就需要等待了。通过分析可以发现计算中有大量重复的计算,比如f(n-2),计算f(n)的时候计算一次,计算f(n-1)的时候又计算一次。可以通过建立缓存来解决重复计算问题。代码如下
import java.util.Scanner;class Fibonacci{ //int能表示的最大值的一半,两个小于halfMax的值相加结果不会越界 private static int halfMax = Integer.MAX_VALUE>>1; //保存求解中的值 private static int value[]; //value中的值是否被计算 private static boolean used[]; //求斐波那契数列第n项,结果对m求余 private static int getFibonacci(int n,int m) { if(used[n]) return value[n]; if(m<=halfMax){ int result = getFibonacci(n-1,m) + getFibonacci(n-2,m); //return result%m; //本来应该进行求余操作,由于result不会大于2m,这里用减法速度回更快 result = result>=m?result-m:result; value[n] = result; used[n] = true; return result; } else{ long result = (long)getFibonacci(n-1,m) + getFibonacci(n-2,m); result = result>=m?result-m:result; value[n] = (int)result; used[n] = true; return value[n]; } } public static int fibonacci(int n,int m) { if(n<1) return 0; if(n==1 || n==2) return 1; value = new int[n+1]; value[0] = 0; value[1] = 1; value[2] = 1; used = new boolean[n+1]; used[0] = true; used[1] = true; used[2] = true; return getFibonacci(n,m); }}public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); System.out.println(Fibonacci.fibonacci(n, 19999997)); }}
斐波那契数列每一项等于前两项的和,我们可以从前往后来计算,使用循环就可以解决了。
import java.util.Scanner;class Fibonacci{ //求斐波那契数列第n项,结果对m求余 public static int fibonacci(int n,int m) { if(n<1) return 0; if(n==1 || n==2) return 1; int value[] = new int[n+1]; value[1] = 1; value[2] = 1; //后一项等于前两项的和 for(int i=3;i<=n;i++){ long result = (long)value[i-1]+value[i-2]; value[i] = (int) (result>m?result-m:result); } return value[n]; }}public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); System.out.println(Fibonacci.fibonacci(n, 19999997)); }}
上面的方法计算的时候开辟的大块空间存储中间计算结果。如果需要经常求不大的数的斐波那契数列,我们可以把一定范围内的结果保存起来,每次需要的时候拿出来就可以了。如果只让我们求一次斐波那契数列的第n项,或者n比较大,上面的方法就不好使了。观察发现我们只需要3个数的空间就够了,每次计算f(n)时f(n-3)就没用了,可以把存放f(n-3)的空间用来存放f(n)。使用循环数组可以实现,这里为了不计算mod3,把数组长度设置为了4。
import java.util.Scanner;class Fibonacci{ //求斐波那契数列第n项,结果对m求余 public static int fibonacci(int n,int m) { if(n<1) return 0; if(n==1 || n==2) return 1; //循环队列用来存放计算结果,这里用4不用3为了计算方便 int value[] = new int[4]; value[1] = 1; value[2] = 1; //后一项等于前两项的和 for(int i=3;i<=n;i++){ long result = (long)value[(i-1)&3]+value[(i-2)&3]; value[i&3] = (int) (result>m?result-m:result); } return value[n&3]; }}public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); System.out.println(Fibonacci.fibonacci(n, 19999997)); }}
如果一般小公司面试写成这样就可以了,如果面试大公司需要写出log(n)时间复杂度的算法才行。对于像类似
f(n) = a*f(n-1)+b*f(n-2)+c*f(n-3)…这样的公式的求值可以用矩阵乘法来完成。由于矩阵乘法有结合律,A的n次方可以用A的n/2次方来求,时间复杂度可以降到log(n)的级别。
斐波那契数列的矩阵计算公式如下所示:
继续进行递推可得下面公式:
这样就可以写一个矩阵计算的类来求斐波那契数列了
import java.util.Scanner;//方形矩阵class Matrix{ private long arr[][]; Matrix(int size){ arr = new long[size][size]; } Matrix(){ arr = new long[2][2]; } //往矩阵的n行m列赋值 public void setValue(int n,int m,int value){ arr[n][m] = value; } public long getValue(int n,int m){ return arr[n][m]; } public int size(){return arr.length;} //该矩阵乘matrix public Matrix muliMatrix(Matrix matrix,int m){ if(this.size() != matrix.size()) return null; Matrix result = new Matrix(this.size()); for(int i=0;i<this.size();i++) for(int j=0;j<matrix.size();j++) for(int k=0;k<this.size();k++){ result.arr[i][j] += this.arr[i][k]*matrix.arr[k][j]; //中间结果对m求余 result.arr[i][j] %= m; } return result; } //矩阵幂运算,n表示幂,m用来求余 public Matrix matirxPower(int n , int m){ if(n==1) return this; Matrix result = new Matrix(this.size()); result = matirxPower(n>>1 , m); result = result.muliMatrix(result , m); if((n&1) == 1){ result = result.muliMatrix(this, m); } return result; }}class Fibonacci{ public static int fibonacci(int n,int m){ if(n<1) return 0; if(n==1 || n==2) return 1; Matrix factor = new Matrix(2); factor.setValue(0, 0, 1); factor.setValue(0, 1, 1); factor.setValue(1, 0, 1); factor = factor.matirxPower(n-2, m); Matrix init = new Matrix(2); init.setValue(0, 0, 1); init.setValue(1, 0, 1); Matrix result = factor.muliMatrix(init, m); return (int) result.getValue(0, 0); }}public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); System.out.println(Fibonacci.fibonacci(n+1, 19999997)); }}
- 斐波那契数列面试题解法(java)
- 面试题斐波那契数列
- 面试题11:斐波那契数列(offer)
- 九度剑指Offer面试题9:斐波那契数列(Java题解)
- 剑指Offer面试题9(java版)斐波那契数列
- 剑指Offer面试题9(java版):斐波那契数列
- 剑指Offer面试题9(java版)斐波那契数列
- 斐波那契数列的矩阵解法(java实现)
- 面试题整理-斐波那契数列
- 面试题9:斐波那契数列
- 【面试题九】斐波那契数列
- 【剑指offer】面试题9:斐波那契数列
- 剑指offer 面试题9 斐波那契数列
- 剑指Offer:面试题9 斐波那契数列
- 面试题9 斐波那契数列
- 面试题9:斐波那契数列
- 面试题9 斐波那契数列
- 面试题9:斐波那契数列
- 链表问题---复制含有随机指针节点的链表
- 判断a,b,c的值能否构成一个三角形
- java类图标变成空心的解决办法
- Linux No rule to make target OR cannot find -l
- FlexSim7.5使用手册目录
- 斐波那契数列面试题解法(java)
- Android app工程中启动 新的 app工程
- springboot下dubbo2.8.4服务的提供与消费(A模块调用B模块提供的服务并对外提供新的服务)
- fastText 源码分析
- android sdk | API level 24 | R
- Mbps 和MB/s 之间的区别
- P1425 小鱼的游泳时间
- sql 各种连接的含义,图示
- hadoop mapreduce 参数优化