zoukankan      html  css  js  c++  java
  • 剑指offer系列——7.斐波拉契数列

    Q:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
    n<=39
    C:时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 32M,其他语言64M
    A:最简单的就是递归……

        int Fibonacci(int n) {
            if (n == 1 || n == 2)
                return 1;
            else if (n == 0)
                return 0;
            else {
                return Fibonacci(n - 1) + Fibonacci(n - 2);
            }
        }
    

    T:
    评论区有提到,最好使用循环,因为递归的本质是栈,容易导致栈溢出。

        int Fibonacci(int n) {
            if (n == 1 || n == 2)
                return 1;
            if (n == 0)
                return 0;
            int temp = 0;
            int n1 = 1;
            int n2 = 1;
            for (int i = 3; i <= n; i++) {
                temp = n2;
                n2 = n2 + n1;
                n1 = temp;
            }
            return n2;
        }
    

    还有使用动态规划法的:(或只需要两个变量存储,把res[i - 1] 和 res[i - 2]换成两个临时变量就好)用动态规划做很省内存,如果用递归,如果n特别大,容易栈溢出

        int Fibonacci(int n) {
            vector<int> res(n + 1, 0);
            res[0] = 1;
            res[1] = 1;
            for (int i = 2; i <= n; ++i) {
                res[i] = res[i - 1] + res[i - 2];
            }
            return res[n];
        }
    

    这是比较通俗的写法,但里面有很多的重复计算。如图,f(18),f(17)都重复计算过。

    因此,放入一个备忘录,就是记录,如果有算过就不再算第二次。

        public int fib(int n) {
            int[] memo = new int[n+1];
            if(n<2)
                return n;
            Arrays.fill(memo,-1);
            memo[0] = 0;
            memo[1] = 1;
            return helper(memo,n);
        }
        private int helper(int[] memo, int n){
            if(memo[n]!=-1)
                return memo[n];
            memo[n] = (helper(memo, n-1)+helper(memo,n-2))%(1000000007);
            return memo[n];
        }
    

    另外,使用尾递归也是避免栈溢出的方法:

        public int Fibonacci(int n) {
            return Fibonacci(n,0,1);
        }
         
         
        private static int Fibonacci(int n,int acc1,int acc2){
            if(n==0) return 0;
            if(n==1) return acc2;
            else     return Fibonacci(n - 1, acc2, acc1 + acc2);
             
        }
    

    P.S. 可以查看递归与尾递归的总结:https://www.cnblogs.com/xym4869/p/12240601.html
    使用矩阵快速幂实现时间复杂度为O(log n)的方法(@elseyu):

    /*
         * O(logN)解法:由f(n) = f(n-1) + f(n-2),可以知道
         * [f(n),f(n-1)] = [f(n-1),f(n-2)] * {[1,1],[1,0]}
         * 所以最后化简为:[f(n),f(n-1)] = [1,1] * {[1,1],[1,0]}^(n-2)
         * 所以这里的核心是:
         * 1.矩阵的乘法
         * 2.矩阵快速幂(因为如果不用快速幂的算法,时间复杂度也只能达到O(N))
         */
    public class Solution {
        public int Fibonacci(int n) {
            if (n < 1) {
                return 0;
            }
            if (n == 1 || n == 2) {
                return 1;
            }
            //底
            int[][] base = {{1,1},
                            {1,0}};
            //求底为base矩阵的n-2次幂
            int[][] res = matrixPower(base, n - 2);
            //根据[f(n),f(n-1)] = [1,1] * {[1,1],[1,0]}^(n-2),f(n)就是
            //1*res[0][0] + 1*res[1][0]
            return res[0][0] + res[1][0];
        }
         
        //矩阵乘法
        public int[][] multiMatrix(int[][] m1,int[][] m2) {
            //参数判断什么的就不给了,如果矩阵是n*m和m*p,那结果是n*p
            int[][] res = new int[m1.length][m2[0].length];
            for (int i = 0; i < m1.length; i++) {
                for (int j = 0; j < m2[0].length; j++) {
                    for (int k = 0; k < m2.length; k++) {
                        res[i][j] += m1[i][k] * m2[k][j];
                    }
                }
            }
            return res;
        }
        /*
         * 矩阵的快速幂:
         * 1.假如不是矩阵,叫你求m^n,如何做到O(logn)?答案就是整数的快速幂:
         * 假如不会溢出,如10^75,把75用用二进制表示:1001011,那么对应的就是:
         * 10^75 = 10^64*10^8*10^2*10
         * 2.把整数换成矩阵,是一样的
         */
        public int[][] matrixPower(int[][] m, int p) {
            int[][] res = new int[m.length][m[0].length];
            //先把res设为单位矩阵
            for (int i = 0; i < res.length; i++) {
                res[i][i] = 1;
            } //单位矩阵乘任意矩阵都为原来的矩阵
            //用来保存每次的平方
            int[][] tmp = m;
            //p每循环一次右移一位
            for ( ; p != 0; p >>= 1) {
                //如果该位不为零,应该乘
                if ((p&1) != 0) {
                    res = multiMatrix(res, tmp);
                }
                //每次保存一下平方的结果
                tmp = multiMatrix(tmp, tmp);
            }
            return res;
        }
         
    }
    
  • 相关阅读:
    1046 Shortest Distance (20 分)(模拟)
    1004. Counting Leaves (30)PAT甲级真题(bfs,dfs,树的遍历,层序遍历)
    1041 Be Unique (20 分)(hash散列)
    1036 Boys vs Girls (25 分)(查找元素)
    1035 Password (20 分)(字符串处理)
    1044 Shopping in Mars (25 分)(二分查找)
    onenote使用小Tip总结^_^(不断更新中...)
    1048 Find Coins (25 分)(hash)
    三个故事
    领导者的举止
  • 原文地址:https://www.cnblogs.com/xym4869/p/12240538.html
Copyright © 2011-2022 走看看