斐波那契数列:暴力递归改动态规划

斐波那契数列:暴力递归改动态规划!

提示:本期开始仔细讲暴力递归改为动态规划填表问题


题目

求斐波那契数列f(n)
当n=1,或者n=2时,f(n)=1;
其余f(n)=f(n-1)+f(n-2)


一、审题

示例:
n=1,f=1
n=2,f=1
n=3,f=f(n-1)+f(n-2)=1+1=2
n=4,f=f(n-1)+f(n-2)=2+1=3
n=5,f=f(n-1)+f(n-2)=3+2=5

1 1 2 3 5 8 11 19 ……


二、暴力递归代码

这逻辑很简答

求斐波那契数列f(n):
当n=1,或者n=2时,f(n)=1;
其余f(n)=f(n-1)+f(n-2)
手撕太过于easy

//斐波那契数列:1,1,2,3,5,8
    //f(n) = f(n-1) + f(n-2)
    //假如n==100,根据测试,电脑开足马力,计算了将近1分钟还没有出来,我停止了
    //n==45,耗时大概3秒
    //而采用缓存的动态规划方法,直接秒杀出来结果节约了大量时间,刚刚100都没法求,而现在很快出来了
    //如果是暴力递归的尝试:

    public static int f(int n){
        if (n == 1 || n == 2) return 1;
        return f(n - 1) + f(n - 2);
    }

    public static int feibo1(int n){
        if (n < 1) return 0;
        return f(n);
    }

测试结果:

public static void test(){
        System.out.println(feibo1(45));
    }

你在idea上跑一下,自己计时:
半天才出结果

1134903170

说明什么,速度实在是太慢了!!!!!!!!!!

暴力递归改动态规划:傻缓存dp

上面为啥慢????
咱们来分析:
咱们当初求f(43)的时候,把f(42)求过一遍了
现在求f(44)又去把f(43)和f(42)再求一遍,岂不是浪费???
如果当初求f(43)和f(42)的时候,直接拿个表记忆一下,比如记录为dp(43)和dp(42)
现在求f(44) = dp(43)+dp(42),这样不用再去递归了,瞬间就返回,这样的话,速度贼快!!!
在这里插入图片描述

这个dp记忆信息的方法就叫动态规划,动态缓存数据,直接用,加速算法返回的速度,减少运算时间,当然代价就是额外空间!!!
手撕代码实现一下:

//复习斐波那契数列动态规划傻缓存
    public static int f3(int n, int[] dp){
        if (dp[n] != -1) return dp[n];//求过了,直接返回

        if (n == 1) {
            dp[1] = 1;
            return dp[1];
        }
        if (n == 2) {
            dp[2] = 1;
            return dp[2];
        }

        dp[n] = f3(n - 1, dp) + f3(n - 2, dp);

        return dp[n];
    }
    public static int feiBoReview(int n){
        if (n < 1) return 0;

        //傻缓存
        int[] dp = new int[n + 1];
        for (int i = 0; i < n + 1; i++) {
            dp[i] = -1;//默认全-1
        }

        return f3(n, dp);
    }

    public static void test2(){
        System.out.println(feibo2(45));
        System.out.println(feiBoReview(45));
        //这里有一个小问题,当你斐波那契数列数字为100时,int类型的结果已经超过65536这么大,所以结果也不好弄了
        //要变化范围才行
        //所以暂时计算就不要超过那个大结果
    }

    public static void main(String[] args) {
//        test();
        test2();
    }

你计时看看,瞬间出来了

1134903170
1134903170

再尝试一下从左往右填表试试

不就一个n参数吗?
dp[n]就是我们要的结果,咱从1 2 3 --N-1填表,最后取dp[n]就行
在这里插入图片描述
手撕代码填表很简单!!!

//不就一个n参数吗?
    //dp[n]就是我们要的结果,咱从1 2 3 --N-1填表,最后取dp[n]就行
    public static int feiBoReviewDP2(int n){
        if (n < 1) return 0;

        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 1;//初始化
        for (int i = 3; i < n + 1; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];//转移方程,递推
        }

        return dp[n];
    }

    public static void test2(){
        System.out.println(feibo2(45));
        System.out.println(feiBoReview(45));
        System.out.println(feiBoReviewDP2(45));
        //这里有一个小问题,当你斐波那契数列数字为100时,int类型的结果已经超过65536这么大,所以结果也不好弄了
        //要变化范围才行
        //所以暂时计算就不要超过那个大结果
    }

    public static void main(String[] args) {
//        test();
        test2();
    }

所谓转移方程,不就是暴力递归函数中的递归公式吗,咱们用傻缓存dp直接将表格从左往右填写,
填一张表的过程,就是动态规划最经典的代码!!!
结果:

1134903170
1134903170
1134903170

总结

提示:重要经验:

1)暴力递归到动态规划的思想,是非常巧妙的,dp表怎么填,都是根据暴力递归函数来的。
2)暴力递归尝试,从左往右,n++,只需一个变量,dp能动态暂存前面的信息,他们都是不变的,后续位置的表格依赖前面的结果,整个表格填完,最后返回dp[n],速度非常快,空间换时间,这就是dp动态规划的本质!!!
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值