递归和动态规划专题(一)----剑指offer+左程云算法

递归和动态规划专题(一)–剑指offer+左程云算法


(一).斐波那契专题

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

  毫无疑问,大家能想到这个公式:F(n)=F(n-1)+F(n-2);如果使用暴力递归的话时间复杂度过高,达到O(2^N)。对此我们关注下面俩种时间复杂度低的解法!

O(N)解法,从左到右依次求出每一项的值,那么通过顺序计算求出第N项即可!

public class Solution {
    public int Fibonacci(int n) {
        //递归多算了太多的重复结点
        //时间复杂度为O(n)
        int[] result = new int[]{0,1};
        if(n<2){
            return result[n];
        }
        //记录前一个数据
        int one=0;
        //记录后一个数据
        int two=1;
        int rs=0;
        for(int i=2;i<=n;i++){
            rs=one+two;

            one = two;
            two = rs;

        }
        return rs;

    }
}

有矩阵乘法的方法可以将时间复杂度降至O(logN)。求斐波那契数列第N项问题变成了如何求一个矩阵的N次方问题!

   [f(n),f(n-1)]=[f(1),f(2)]*{{1,1},{1,0}}^n-2

public class Solution {
    public int Fibonacci(int n) {

        //边界条件判断
        if(n==0){
            return 0;
        }
        if(n==1||n==2){
            return 1;
        }
        //设置底数矩阵
        int[][] base = {{1,1},{1,0}};
        int[][] res = matrixPower(base,n-2);

        return res[0][0]+res[1][0]; 

    }

    //求矩阵的N次方的结果
    public int[][] matrixPower(int[][] base,int n){
        int[][] res=new int[base.length][base[0].length];
        //把res设置为单位矩阵,类似于整数中的1;
        for(int i=0;i<res.length;i++){
            res[i][i]=1;
        }

        //类似整数的n次方求法;通过位运算
        while(n!=0){
            if((n&1)==1){
                res=muliMatrix(res,base);
            }

            base=muliMatrix(base,base);
            n>>=1;
        }

        return res;
    }
    public int[][] muliMatrix(int[][] m1,int[][] m2){
        int[][] temp = new int[m1.length][m2[0].length];
        for(int i=0;i<temp.length;i++){
            for(int j=0;j<temp[0].length;j++){
                for(int k=0;k<m2.length;k++){

                     temp[i][j]+=m1[i][k]*m2[k][j];
                }

            }
        }

        return temp;

    }
}

数值的整数次方

【题目】给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

public class Solution {
    public double Power(double base, int exponent) {
        /**此题为简单快速幂问题
        *1.exponent 为负数,0,整数都得分开考虑
        *2.10^13 =10^1101 = 10^0001*10^0100*10^1000
        *3.通过&1和>>移位来逐位读取1,对改为代表的乘数累乘
        **/

        int n=0;
        if(exponent>0){
            n=exponent;
        }else if(exponent<0){
            if(equal(base,0.0)){
                return 0.0;
            }
            //取绝对值
            n=-exponent;
        }else{ //exponent==0
           if(equal(base,0.0)){
                return 0.0;
            }
            return 1;
        }
        double res=1;
        while(n!=0){
            if((n&1)==1){
                res*=base;
            }
            base*=base;
            n>>=1;

        }
        return exponent>0?res:(1.0/res);


  }
    //在判断底数为0时,不能直接写base==0,这是因为在计算机内表示小数时(包括float,double型小数)都有误差。
    boolean equal(double num1,double num2){
        if((num1-num2>-0.00000001)&&(num1-num2 <0.0000001)){
            return true;
        }else{
            return false;
        }
    }
}

跳台阶

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

//O(N)
public class Solution {
    public int JumpFloor(int target) {
        int n=target;
        //n<=2时
        int[] result = new int[]{0,1,2};
        if(n<=2){
            return result[n];
        }

        int one=1;
        int two=2;
        int rs=0;
        for(int i=3;i<=n;i++){
            rs = one+two;
            one = two;
            two = rs;
        }
        return rs;

    }
}

***************O(log n)时间复杂度*****************

public class Solution {
    public int Fibonacci(int n) {

        //边界条件判断
        if(n==0){
            return 0;
        }
        if(n==1||n==2){
            return n;
        }
        //设置底数矩阵
        int[][] base = {{1,1},{1,0}};
        int[][] res = matrixPower(base,n-2);

        return 2*res[0][0]+res[1][0]; 

    }

    //求矩阵的N次方的结果
    public int[][] matrixPower(int[][] base,int n){
        int[][] res=new int[base.length][base[0].length];
        //把res设置为单位矩阵,类似于整数中的1;
        for(int i=0;i<res.length;i++){
            res[i][i]=1;
        }

        //类似整数的n次方求法;通过位运算
        while(n!=0){
            if((n&1)==1){
                res=muliMatrix(res,base);
            }

            base=muliMatrix(base,base);
            n>>=1;
        }

        return res;
    }
    public int[][] muliMatrix(int[][] m1,int[][] m2){
        int[][] temp = new int[m1.length][m2[0].length];
        for(int i=0;i<temp.length;i++){
            for(int j=0;j<temp[0].length;j++){
                for(int k=0;k<m2.length;k++){

                     temp[i][j]+=m1[i][k]*m2[k][j];
                }

            }
        }

        return temp;

    }
}

变态跳台阶

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

public class Solution {
    public int JumpFloorII(int target) {

        //通过举例加上数学归纳法可以得出结论  f(n)=2^(n-1);
        if(target ==0){
            return 0;
        }else{
            //熟练使用位运算
            return 1<<--target;
        } 
    }
}

矩形覆盖

【题目】我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

这个是O(N)的写法,,O(log n)的写法和上面题目一模一样!

public class Solution {
    public int RectCover(int target) {

        //这个是变相的斐波那契数列
        //f(n)为竖放时,右边剩余区域的方法f(n-1),横放时,右边为f(n-2)
        int n=target;
        int[] result = new int[]{0,1,2};
        if(n<=2){
            return result[n];
        }
        int one=1;
        int two=2;
        int rs=0;
        for(int i=3;i<=n;i++){

            rs=one+two;
            one = two;
            two=rs;
        }
        return rs;
    }
}

【注意】如果递归式子严格符合F(n)=a*F[n-1]+b*F[n-2]+….+k*F[n-i],那么它就是一个i阶递推式,必然有与i*i状态矩阵相关的乘法的表达。一律可以用加速矩阵乘法的动态规划将时间复杂度变为O(log N)。

【题目】假设农场中成熟的母牛每年只会生1头小母牛,并且永远不会死。第一年农场有1只成熟的母牛,从第二年开始,母牛将开始生小母牛。每只小母牛3年之后成熟又可以开始生小母牛。给定整数N,求出N年后牛的数量。

[F(n),F(n-1),F(n-2)] = [F[1],F(2),F(3)]*{{1,1,0},{0,0,1},{1,0,0}}^n-3; 具体代码实现参考上面题目中的代码!

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页