算法复习:由爬楼梯问题引发的斐波那契数列的 递归、动态规划的实现的思考

问题引出:
leetcode NO.70

再做这类题的时候 如果无从下手:

可以考虑
1.暴力法能否解决 不行的话就考虑下面的方法
2.因为问题比较复杂 比较绕 那么就先只看基本情况 在考虑采用数学归纳法 归纳出一般性
3.找逻辑 找最近重复子问题,也就是要去找问题中重复的部分
——可以考虑采用递归 或动态规划

递归

1.最普通的傻递归 O(2^n) 而且可能引发StackOverflow

// 方法一:直接递归  
// 但是这道题有点和斐波那契数列稍稍不符合的地方是 f0=0 而数列中f0=1;
class Solution {
    public int climbStairs(int n) {
          
         if(n == 0) { //不同处 f0=0,它有点点不符合斐波那契数列的思想 而数列中f0=1;
             return 0;
         }   
         if(n == 1) {
            return 1;
         }
         if(n == 2) {
          	return 2;
         }
            return  climbStairs(n-2) + climbStairs(n-1); 
         }  
    }

2.加数组缓存的递归 (前人栽树后人乘凉LOL)

//这个方法是可以的!! 但是只是适用于在输入44之前的数
// 方法二:记忆化递归 有缓存的思想 创一个数组 每次把递归的结果记录下来 方便下一次递归使用
//每当函数再次被递归调用时,我们就直接从 memo 数组返回结果。
// 但是这道题有点和斐波那契数列稍稍不符合的地方是 f0=0 而数列中f0=1;
class Solution {
    public int climbStairs(int n) {
         int memo[] = new int[n + 1];//防止递归时 溢出
         if(n == 0) { //不同处 f0=0,它有点点不符合斐波那契数列的思想 而数列中f0=1;
             memo[0] = 0;
             return 0;
         }   
         if(n == 1) {
             memo[1] = 1;
             return 1;
         }
         if(n == 2) {
             memo[2] = 2;
             return 2;
         }
         //接下来 和普通递归不同处是 每次先找缓存里面找我需要的递归式 是否已经在上一步算出来并且缓存了
         if(memo[n] != 0) { //因为java数组默认全部填0  那么我这里如果判断n这个位置不是0 说明已经缓存过 
            return memo[n]; //那就返回即可
         } else { //说明之前没缓存 需要缓存 给后面用 (前人栽树后人乘凉LOL)
            int rst = climbStairs(n-2) + climbStairs(n-1); 
            memo[n] = rst;
            return rst; 
         }  
    }
}

3.方法2plus 使用hashmap的缓存的递归

//这个方法也是可以的!! 但是只是适用于在输入44之前的数
// 方法三:记忆化递归plus 有缓存的思想 创一个数组 每次把递归的结果记录下来 方便下一次递归使用
//每当函数再次被递归调用时,和前一种方法的存储不同,前一种我们是从 数组memo[] 返回结果。 而现在可以使用hashmap(好处是使用它内部的查询优化)
// 但是这道题有点和斐波那契数列稍稍不符合的地方是 f0=0 而数列中f0=1;
class Solution {
    public int climbStairs(int n) {
        //  int memo[] = new int[n + 1];//防止递归时 溢出 不用它了 用hashmap
      Map<Integer, Integer> memo = new HashMap<>();
         if(n == 0) { //不同处 f0=0,它有点点不符合斐波那契数列的思想 而数列中f0=1;
             memo.put(0,0);
             return 0;
         }   
         if(n == 1) {
             memo.put(1,1);
             return 1;
         }
         if(n == 2) {
             memo.put(2,2);
             return 2;
         }
         //接下来 和普通递归不同处是 每次先找缓存里面找我需要的递归式 是否已经在上一步算出来并且缓存了
         if(null != memo.get(n)) { //能取到值 说明已经缓存过 
            return memo.get(n); //那就返回即可
         } else { //说明之前没缓存 需要缓存 给后面用 (前人栽树后人乘凉LOL)
            int rst = climbStairs(n-2) + climbStairs(n-1); 
            memo.put(n,rst);
            return rst; 
         }  
    }
}

4.方法四:动态规划(自底向上的动态规划) 还是 创一个数组 每次把规划的结果(子问题的解)记录下来 方便下一次父问题使用
实际上动态规划还有一种备忘录法(自顶向下)

class Solution {
    public int climbStairs(int n) {
        // int dp[] = new int[n+1];//当n取1 这就会报错 所以可以再开大点
        int dp[] = new int[n+2]; 
        if (n == 0) {
            return 0;
        }
        // if (n == 1) {
        //     return 1;
        // }
        // if (n == 2) {
        //     return 2;
        // } //实际可以用下一句代替 因为程序最好单一出口 或者少出口 但是写上的话 易读性更好
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3; i <= n; i++) {
            dp[i] = dp[i-2] + dp[i-1];
        }
        return dp[n];
    }
}

// / 方法四:动态规划 还是 创一个数组 每次把规划的结果(子问题的解)记录下来 方便下一次父问题使用
 // 但是这道题有点和斐波那契数列稍稍不符合的地方是 f0=0 而数列中f0=1;

5.动态规划plus (也称斐波那契数) 不需要数组 不需要每次把规划的结果(子问题的解)记录下来
只需要存最后的三个值即可 因为过程中每次子问题的解是动态变换的 也就是说作为解父问题的条件他是不断变换的 那么到了倒数的第二个第三个数 肯定是可以成为最后一个数的子问题的解

//复习前一天:
// 方法五: 动态规划 但不使用循环 无需记录过程中的数 但是只需要记住最后的三个数即可 因为过程中每次子问题的解是动态变换的  也就是说作为解父问题的条件他是不断变换的 那么到了倒数的第二个第三个数 肯定是可以成为最后一个属的子问题的解 
class Solution {
    public int climbStairs(int n) {
        int rst = 0;
        int f1 = 1;
        int f2 = 2;
        if(n == 0) { //不同处 f0=0,它有点点不符合斐波那契数列的思想 而数列中f0=1;  
             return 0;
         }   
         if(n == 1) {
            return 1;
         }
         if(n == 2) {
          	return 2;
         }
         for(int i = 3; i <= n; i++) {
             rst = f2 + f1;
             f1 = f2;
             f2 = rst;
         }   
         return  rst; 
         }          
    }

「递归」和「迭代」有哪些区别?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值