斐波那契数列面试题解法(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));    }}
原创粉丝点击