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