剑指Offer-07-费波纳茨数列

题目

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
n<=39

解析

预备知识

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……
通项公式为:

f ( n ) = { 0 , n == 0 1 , n == 1 f ( n 1 ) + f ( n 2 ) , n > 1

思路一

就是按照通项公式写出递归形式即可。

    /**
     * 普通递归形式
     * @param n
     * @return
     */
    public static int Fibonacci2(int n) {
        if(n <= 1) {
            return n;
        }
        return Fibonacci2(n - 1) + Fibonacci2(n - 2);
    }

思路二

上述递归存在一个问题,会重复计算相同子问题的解。
比如:
f(10) = f(9) + f(8)
f(9) = f(8) + f(7)
f(8) = f(7) + f(6)
f(7) = f(6) + f(5)
我们在计算f(10)的时候需要继续f(8),而在f(9)也要再计算f(8),下面的计算也是,存在大量的重复的计算,复杂度只奔指数级别。这时我们可以把已计算过的子问题的结果记录下来,下次再碰到相同的子问题,直接取值即可。

     static int[] fibo = new int[45];

    /**
     * 记忆化搜索
     * @param n
     * @return
     */
    public static int Fibonacci3(int n) {
        if(n <= 1) {
            return n;
        }
        if(fibo[n] != 0) {
            return fibo[n];
        }
        fibo[n] = Fibonacci3(n - 1) + Fibonacci3(n - 2);
        return fibo[n];
    }

思路三

但记忆化搜索本身还是存在栈溢出的可能,递归的本质就是函数不断调用自身,即函数栈不断进行入栈操作,在递归深度达到一定程度时,必然会超过栈的内存限制,造成栈溢出。所以化递归为循环。

    /**
     * 循环形式
     * @param n
     * @return
     */
    public static int Fibonacci1(int n) {
        int first = 0;
        int second = 1;
        if(n == 0) {
            return first;
        }
        if(n == 1) {
            return second;
        }
        int result = 0;
        for(int i = 2; i <= n; i++) {
            result = first + second;
            first = second;
            second = result;
        }
        return result;
    }

思路四

利用矩阵快速幂来做,复杂度可以达到O(logn)。

矩阵快速幂的原理与普通数的快速幂一样,不清楚可以自行看看快速幂的原理,还是挺简单,利用指数二进制表示来分解乘法。减少了乘法次数。

我们发现f(n) = f(n - 1) + f(n - 2)。对应的矩阵形式为:

[ f ( n ) f ( n 1 ) ] = [ f ( n ) f ( n 1 ) ] [ 1 1 1 0 ]

化简上式可得:
[ f ( n ) f ( n 1 ) ] = [ 1 1 ] [ 1 1 1 0 ] n 2

代码如下:

     /**
     * 基于矩阵快速幂的解法
     * 新颖,快速,牛逼
     * @param n
     * @return
     */
    public static int Fibonacci4(int n) {
        if(n == 0) {
            return 0;
        }
        if(n == 1 || n == 2) {
            return 1;
        }
        int[][] base = {{1, 1}, {1, 0}};
        int[][] res = fastPowMatrix(base, n - 2);
        return res[0][0] + res[1][0];
    }

    /**
     * 矩阵快速幂
     * @param base
     * @param index
     * @return
     */
    public static int[][] fastPowMatrix(int[][] base, int index) {
        int[][] res = new int[base.length][base[0].length];
        for(int i = 0; i < base.length; i++) {
            res[i][i] = 1;
        }
        int[][] t = base;
        while(index != 0) {
            if((index & 1) == 1) {
                res = multipleMatix(res, t);
            }
            t = multipleMatix(t, t);
            index >>= 1;
        }
        return res;
    }

    /**
     * 矩阵相乘
     * @param left
     * @param right
     * @return
     */
    public static int[][] multipleMatix(int[][] left, int[][] right) {
        int[][] res = new int[left.length][left[0].length];
        for(int i = 0; i < left.length; i++) {
            for(int j = 0; j < right.length; j++) {
                for(int k = 0; k < left[0].length; k++) {
                    res[i][j] += left[i][k] * right[k][j];
                }
            }
        }
        return res;
    }

猜你喜欢

转载自blog.csdn.net/dawn_after_dark/article/details/80686892
今日推荐