zoukankan      html  css  js  c++  java
  • 剑指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);
        }
    
    }
  • 相关阅读:
    Zojax.com
    holtwick/pyxer
    深入学习爬虫的三个方向
    阅读gunicorn代码文档¶
    A database of opensource HTTP proxies written in python: htfilter2
    WebDebug: Enhancements
    北京网康科技有限公司关于网康
    用CONNECT方法突破HTTP代理服务器的扩展名封锁(python) [转] MyNewID 博客园
    NAT功能的用途是什么?有没有实例举一下?_百度知道
    wsgiref — WSGI Utilities and Reference Implementation¶
  • 原文地址:https://www.cnblogs.com/codingexperience/p/5821750.html
Copyright © 2011-2022 走看看