深入斐波那契类型问题19.9.10

本文深入探讨了使用斐波那契思想解决阶梯跳法问题,提出递归算法并分析其效率,通过改进实现避免重复计算,提高算法性能。同时,将这种方法扩展到更复杂的跳跃问题和矩形覆盖问题,强调在遇到此类问题时寻找规律、确定递归出口和优化时间复杂度的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.题目描述I

       一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)

 

1.1 解题思路

       这和斐波那契数列是同一个思想,可以倒过来看:我要走完n层,我有两种方式,在n-1层走1步或者n-2层走2步。依次往下推导可得到这样一个表达式:

f(n) = f(n-1)+f(n-2);

但是作为递归,需要一个出口:

f(1) = 1; f(2) = 2;

1.2 code: java

//递归最复杂的方式,但题目限制了我们改进
public static int JumpFloor(int target) {
        if(target == 1 || target == 2)
                return target;
        else return JumpFloor(target-1)+JumpFloor(target-2);
    }
//也有用递推的思想
int jumpFloor(int target) {
        int t1=1,t2=2,total=0;
        if (target==1||target==2) return target;
        for(int i=3;i<=target;i++) {
            total=t1+t2;
            t1=t2;
            t2=total;
        }
        return total;
    }

 

1.3 分析:

       上述代码都能过,但是有一个很大的问题。都有重复计算的过程,如果题目传的参数改变,换成数组,那么第二种里面把for循环里的改成数组存储,那也能做到优化。

 

下面我自己试着改变题目条件,重写一遍,用剪枝的思想减少时间复杂度。

通过画图先分析之前的算法:

                                 图1 常规递归运算过程

 

       可以明显地看到有很多是重复的,这样运算的复杂度当然会很大,所以对递归的题目,如果我们有了存储,那复杂度会大大降低,这个方法是通过数组方式来存储和标记,具体计算的过程就是下图2了:

                                             图2 改进递归的计算过程

 

1.4 code:java改进  

第一种:数组跟踪标记

package 剑指offer;
public class jumpFloorII {
     
     static int[] arr= new int[50];
     static int[] boolean_arr = new int[50];
     
     public static int JumpFloorII(int target,int[]  arr,int[] boolean_arr) {
        /**
         * 以下程序1和2需要互换位置,以免JumpFloorII(2)重复计算
         */
        //程序①
        if(target==1 || target==2){
          arr[target] = target;
          boolean_arr[target]++;
            return target;
        }
        //程序②
        if(boolean_arr[target]!=0) {
          return arr[target];
        }

        else{
          arr[target] =  JumpFloorII(target-1,arr,boolean_arr)+JumpFloorII(target-2,arr,boolean_arr);
          boolean_arr[target]++; 
        }   
        return arr[target];
    }

     public static void main(String[] args) {
          
          int target=10;
          System.out.println(JumpFloorII(target,arr,boolean_arr));
          for(int i=1;i<=target;i++) {
              System.out.println("arr"+"["+i+"]"+"="+arr[i]);
              System.out.println("boolean_arr"+"["+i+"]"+"="+boolean_arr[i]);
          }
     }
}
/*我通过打印boolean_arr数组,来观察其是否经过重复运算,1则表示只进行一次,所以这样看来,实现了我们所要的效果
89
arr[1]=1
boolean_arr[1]=1
arr[2]=2
boolean_arr[2]=2  
arr[3]=3
boolean_arr[3]=1
arr[4]=5
boolean_arr[4]=1
arr[5]=8
boolean_arr[5]=1
arr[6]=13
boolean_arr[6]=1
arr[7]=21
boolean_arr[7]=1
arr[8]=34
boolean_arr[8]=1
arr[9]=55
boolean_arr[9]=1
arr[10]=89
boolean_arr[10]=1
*/
但是应该都能注意到为什么boolean_arr【2】=2,这是因为在target=4的时候,它分出了f(3)和f(2),一边f(3)去递归,得到f(2)和f(1),这里boolean_arr【2】++,加了一次,一边f(2)右加了一次,因为是遇到f(2)直接返回的,所以boolean_arr【2】=2。为了规避这个问题,只需要把
程序①和程序②调换一下位置即可。

//第二种:更方便简洁

public static int JumpFloorII(int target,int[]  arr) {
   arr[1]=1;
   arr[2]=2;
   for(int i=3;i<=target;i++){
      arr[i]=arr[i-1]+arr[i-2];
}
   return arr[target];
}

2 知识延伸

① 对于上述的第二种,传入的数组也可以用C++的vector,其本质也是数组。

② 遇到数据量特别大的,可以考虑用java的BigInteger来做。

参考:https://blog.youkuaiyun.com/kazama_kenji/article/details/52330338

 

3 题目描述II

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

3.1 解题思路

      寻找规律,有了上面那题的引导,我们继续循着逆向思考,从N开始,直接一步算一种,N-1->N,需要f(n-1)种,N-2需要f(n-2)种,注意,对于这题,跳的步数不再是1步或者2步了,而是n步。所以需要继续加上f(n-3)……

所以递归公式就出来了:

f(n) = 1+f(n-1)+f(n-2)+……f(1);

递归出口:

f(1)=1; f(2)=2;

3.2 code: java   

public class Solution {
    public int JumpFloorII(int target) {
        int tot=0;
        if(target==1 || target==1){
            return target;
        }
        else for(int i=1;i<=target-1;i++) {
            tot += JumpFloorII(i);
        }
        return tot+1;
    }
}

 

4 题目描述-矩形覆盖

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

4.1 解题思路:一样,先找规律,再找出口

4.2 code java

public class Solution {
    public int RectCover(int target) {
        if(target == 0)
            return 0;
        if(target == 1 ||target == 2)
            return target;
        return RectCover(target-1)+RectCover(target-2);
    }
}

 

5 总结:

遇到类似问题,先找规律,得到通式,然后寻找递归出口。接下来可以考虑具体实现方式:可以从时间复杂度方面去改进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值