当前位置: 首页 > article >正文

动态规划入门(记忆化搜索)——爬楼梯(Leetcode 70题)

动态规划

动态规划是通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法,换而言之:这个问题的解决可以通过解决许多个小的子问题来构建,而这些子问题会重复出现。

记忆化搜索

入门的动态规划的基础是记忆化搜索,或者说记忆化搜索是动态规划的一种,而动态规划是一种更广泛的概念,它包括了记忆化搜索记。忆化搜索是动态规划在递归算法中的一种实现方式。动态规划可以不使用递归,而记忆化搜索通常与递归结合使用。两种方法都是在通过存储中间结果来避免重复计算,从而提高算法的效率。

值得注意的是:

记忆化搜索并不是万能的,多数题目都要通过递归来实现,因此在时间复杂度上可能并不优秀,尽管可以通过一些数据结构来优化时间和空间复杂度,但是还是应该值得我们去注意。

题目 

. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。icon-default.png?t=O83Ahttps://leetcode.cn/problems/climbing-stairs

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

分析 (递归思想)

 我们每次都只能爬1或2个台阶,因此我们倒过来看这道题目,我们最后上到楼顶前只能有两种情况:

1、走1个台阶,也就是我们需要先得到0——n-1级台阶的方法

2、走2个台阶,也就是我们需要先得到0——n-2级台阶的方法

这样我们就把问题缩小成:

f[n]=f[n−1]+f[n−2]

代码实现:

class Solution {
    public int climbStairs(int n) {
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        return climbStairs(n-1)+climbStairs(n-2);
    }
}

但是很明显的是这样做的时间复杂度为O(2^n),很明显会超时。我们也可以发现整个递归中有大量重复递归调用,例如:

在计算发f[7]时我们的计算过程为:f[7]=f[6]+f[5]  f[6]=f[5]+f[4]……先看前两个式子那么我们是不是就调用了两次f[5]这样的重复是很多的,这也加大了时间复杂度,那么我们可以通过记录每个f[i]的返回值来减少重复调用,也就是递归 + 记录返回值 = 记忆化搜索,这便是记忆化搜索那我们接着来看看记忆化搜索的办法。

记忆化搜索解题

class Solution {
    public int climbStairs(int n) {
        if(n==1) return 1;
        if(n==2) return 2;
        int[] f = new int[n + 1];
        f[1] = 1;f[2] = 2;
        for (int i = 3; i <= n; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f[n];
    }
}

通过记忆化搜索我们的时间复杂度成功降到了 O(n)。

优化

 这里我们创建了一个数组来存储f[i]的返回值,因此空间复杂度为O(n),那么当n足够大时便会出现内存溢出,因此我们还可以优化。通过观察不难发现发现一旦算出 f[i],那么  除了f[i−1]和f[i−2]以外的值我们都不用了,因此我们每次只需要保留f[i−1]和f[i−2]就可以了。

class Solution {
    public int climbStairs(int n) {
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        int a=1;
        int b=2;
        for(int i=3;i<=n;i++){
            int c=a+b;
            a=b;
            b=c;
        }
        return b;
    }
}


http://www.kler.cn/a/400075.html

相关文章:

  • 【C++滑动窗口】1248. 统计「优美子数组」|1623
  • 文心一言编写小球反弹程序并优化
  • 【Java多线程】单例模式(饿汉模式和懒汉模式)
  • 问:说说SpringDAO及ORM的用法?
  • 【软件测试】设计测试用例的万能公式
  • 数据集的重要性:如何构建AIGC训练集
  • 【WPF】Prism学习(六)
  • PgSQL即时编译JIT | 第1期 | JIT初识
  • 【C++之STL】摸清 string 的模拟实现(上)
  • PlantUML——时序图
  • Python实现ARIMA模型
  • 如何使用 Vivado 从源码构建 Infinite-ISP FPGA 项目
  • vue项目PC端和移动端实现在线预览docx、excel、pdf文件
  • 配置Nginx实现用IP测试灰度发,通过不同用户ID测试灰度发布
  • Flutter踩坑:原生安卓页面向Flutter通信
  • android通过广播设置默认启动器
  • 【Pikachu】XML外部实体注入实战
  • Loopy为何成为IP联名新顶流,如何赋能品牌营销新高度?
  • 用Ruby编写一个自动化测试脚本,验证网站登录功能的正确性。
  • TCP/IP协议浅析
  • 前端三大件之CSS
  • opencv调整图片对比度和亮度
  • 大模型(LLMs)推理面
  • 微信小程序点击跳转打电话功能
  • 实操案例|TinyVue树表+动态行合并
  • 【验证码逆向专栏】vaptcha 手势验证码逆向分析