剑指Offer——跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

思路分析

    这个问题可以先从简单开始考虑,台阶只有1阶,只有1种跳法,台阶有2阶,有2种跳法:一种两次跳一级;另一种一次跳两级。然后考虑一般情况,当有n级台阶时,将f(n)作为总跳法,第1次跳的时候,可以有两种方法:一是跳一级,此时跳法数目等于后面剩下的n-1级台阶的跳法,即f(n-1);二是跳两级,此时跳法数目等于后面剩下的n-2级台阶的跳法。所以,n级台阶的跳法总数:f(n)=f(n-1)+f(n-2)。从这里可以看出,本题是斐波那契数列的一种变形。

   但本题还有一种思路,就是将跳1级与跳2级看成排列组合问题,假设x为跳1级台阶的次数,y为跳2级台阶的次数,于是可知x+2y=n,即x=n-2y,这里可以看作对x与y的排列组合问题,即C(x+y,y)=C(n-y,y)。其中0<=y<=n/2

代码实现

斐波那契数列实现

1、递归实现

    相信很多人第一次接触到斐波那契数列的时候,就是使用递归实现,该实现简单直观,但该算法效率不高,因为递归会反复计算相同的子问题,随着n的增大,计算量也急剧增大,时间复杂度为T(n) = O(1.618 ^ n)。代码如下:

 public int recurFib(int n) {
        if (n <= 0) {
            return 0;
        }
        if (n == 1 || n == 2) {
            return n;
        }
        return recurFib(n - 1) + recurFib(n - 2);
    }

2、递推实现

    使用递归实现的算法之所以效率太低,是因为重复计算太多,所以我们可以将中间结果保存,当再次计算的时候先查找一下。但有一种更简单的方法,就是从下向上计算,递归是从上到下计算,上面会依赖下面的值,因此会导致重复计算。我们使用从下往上计算,没有值依赖,算法复杂度就降为O(n)。首先根据f(1)和f(2)算出f(3),f(2)和f(3)算出f(4),以此类推即可算出f(n),实现代码如下:

  public int recursiveFib(int n) {
        if (n <= 0) {
            return 0;
        }
        if (n == 1 || n == 2) {
            return n;
        }
        int fibN = 0;
        int fibNMinusOne = 2;
        int fibNMinusTwo = 1;
        for (int i = 3; i <= n; i++) {
            fibN = fibNMinusOne + fibNMinusTwo;

            fibNMinusTwo = fibNMinusOne;
            fibNMinusOne = fibN;
        }
        return fibN;
    }

3、矩阵法实现

    还有一种矩阵法实现,算法效率最高,时间复杂度为O(logn),该算法是根据下面的公式:

    上面的公式可以数学归纳法证明,感兴趣的可以自己搜索一下相关资料,有了上面的公式,就将求解f(n),转换成求二阶矩阵的n次方,然后取结果的第1行第2列即可。如果仅仅是矩阵的乘法,时间复杂度依旧为O(n),但可以使用快速幂的方法求解一个数的乘法。乘方的性质如下:

image

     结合上面两个公式代码如下:

public class Solution {

    public int fib(int n) {
        if (n <= 0) {
            return 0;
        }
        if (n <= 2) {
            return n;
        }
        int[][] unitMatrix = {{1, 1}, {1, 0}};
        int[][] result = matrixPow(unitMatrix, n);
        return result[0][0];
    }

    public int[][] matrixPow(int[][] mat, int n) {
        if (n == 1) {
            return mat;
        } else {
            // n是偶数
            if ((n & 1) == 0) {
                int[][] temp = matrixPow(mat, n >> 1);
                return matrixMultiply(temp, temp);
            } else {
                // n是奇数
                int[][] temp = matrixPow(mat, (n - 1) >> 1);
                return matrixMultiply(matrixMultiply(temp, temp), mat);
            }
        }
    }

    /**
     * 矩阵相乘
     *
     * @param m
     * @param n
     * @return 结果矩阵,m*n
     */
    public int[][] matrixMultiply(int[][] m, int[][] n) {
        int rows = m.length;
        int cols = n[0].length;
        int[][] r = new int[rows][cols];

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                r[i][j] = 0;
                for (int k = 0; k < m[i].length; k++) {
                    r[i][j] += m[i][k] * n[k][j];
                }
            }
        }
        return r;
    }

    public static void main(String args[]) {
        Solution s = new Solution();
        int result = s.fib(3);
        System.out.println(result);
    }
}

排列组合实现

    排列组合是将跳1级数目和跳2级数目看出一个组合,其中x为跳1级数目,y为跳2级数目,且x+2y=n,问题转换成从(x+y)个总跳数中选出y个跳2级的的组合,即C(x+y,y)=C(n-y,y)。实现代码如下:

public class Solution {

    public BigDecimal jiecheng(int number) {
        BigDecimal result = new BigDecimal(1);
        BigDecimal temp;
        for (int i = number; i > 0; i--) {
            temp = new BigDecimal(i);
            result = result.multiply(temp);
        }
        return result;
    }

    public int fib(int target) {
        if (target <= 0) {
            return 0;
        }
        BigDecimal result = new BigDecimal(0);
        // the result is C(n-y,y)
        for (int i = 0; i <= target / 2; i++) {
            result = result.add(jiecheng(target - i).divide(jiecheng(target - 2 * i).multiply(jiecheng(i))));
        }
        return result.toBigInteger().intValue();
    }

    public static void main(String args[]) {
        Solution s = new Solution();
        int result = s.fib(29);
        System.out.println(result);
    }

}
posted @ 2016-08-30 13:58  苍穹2018  阅读(691)  评论(0编辑  收藏  举报