1. 程式人生 > >遞迴和動態規劃專題(一)----劍指offer+左程雲演算法

遞迴和動態規劃專題(一)----劍指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; 具體程式碼實現參考上面題目中的程式碼!