算法力扣刷题记录 七十【70. 爬楼梯及算法性能分析:时间复杂度和空间复杂度】

news2025/1/18 3:21:07

前言

动态规划章节第二篇。记录 七十【70. 爬楼梯】


一、题目阅读

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

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

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶

提示:

1 <= n <= 45

二、尝试实现

分析题目

  1. 拿到题目分析应该用什么方法:有递归(回溯)、贪心算法、动态规划。首先分析题目:刚拿到时,直给了两个示例,看不出来什么,那么就多写几个n,找找规律。
  • 当n=1时,1种方案:{1}
  • 当n=2时,2种方案:{1,1}和{2};
  • 当n=3时,3种方案:{1,1,1}和{1,2}和{2,1}
  • 当n=4时,5种方案:{1,1,1,1}和{1,1,2}和{1,2,1}和{2,1,1}和{2,2}
  • 当n=5时,8种方案:{1,1,1,1,1}和{1,1,1,2}和{1,1,2,1}和{1,2,1,1}和{1,2,2}和{2,1,1,1}和{2,1,2}和{2,2,1}
  • 总结:好像有点规律了,是斐波那契数列。递推公式:dp[i] = dp[i-1]+dp[i-2]:dp数组含义是到第(i+1)层时有几种方案,下标i含义是这是第i+1层。

思路1【动态规划法】

  1. 很明显,动态规划:一个状态可以由之前的状态推导而出,应该用动态规划
  • 明确dp数组含义和下标含义:已经指出;
  • 递推公式:已经指出;
  • 初始化:dp[0] = 1;第一层台阶有一种方案。dp[1] = 2;到2层台阶有2中方案;
  • 遍历顺序:从前往后。

代码实现【动态规划法】

class Solution {
public:
    int climbStairs(int n) {
        if(n <= 2) return n;
        vector<int> dp(n,0);//下标i代表到第i+1层台阶,值代表到i+1层台阶有几种方案
        //初始化
        dp[0] = 1;//n=1时,1种方案
        dp[1] = 2;//n=2时,2种方案
        //遍历顺序
        for(int i = 2;i < n;i++){
            //递推公式
            dp[i] = dp[i-1]+dp[i-2];
        }
        return dp[n-1];
    }
};

状态压缩
class Solution {
public:
    int climbStairs(int n) {
        if(n <= 2) return n;
        int dp[2];
        //初始化
        dp[0] = 1;//n=1时,1种方案
        dp[1] = 2;//n=2时,2种方案
        //遍历顺序
        for(int i = 2;i < n;i++){
            int sum = dp[0]+dp[1];
            dp[0] = dp[1];
            dp[1] = sum;
        }
        return dp[1];
    }
};

思路2 【递归】

  1. 能用动态规划法,这是记录 六十九【509. 斐波那契数】中提到递归也可以。通过状态递推公式可以认为在重复执行一段代码。

代码实现【递归法】

逻辑没有问题,但是无法通过。因为超出时间限制

class Solution {
public:
    int climbStairs(int n) {
        //终止条件
        if(n <= 2) return n;
        
        //单层逻辑
        return climbStairs(n-1)+climbStairs(n-2);
    }
};

思路3【回溯法】

  1. 回溯用在组合问题。从一个集合中选择符合条件的元素形成组合可以用。在分析题目时,画树形图判断有几种方案如下:
    在这里插入图片描述
  2. 所以回溯解决办法:每一次都从[1,2]中选择元素加入temp,当组合元素之和是n时符合条件。大于n时停止。

代码实现【回溯法】

逻辑没有问题,但是无法通过。因为超出时间限制

class Solution {
public:
    void backtracking(int n,int& sum,vector<int>& temp,int& result){
        //终止条件
        if(sum == n){
            result++;
            return;
        }
        if(sum > n){//顶多走n个台阶。超出n就可以停止搜索了
            return;
        }
        //在[1,2]两种走法中选择
        for(int i = 1;i <= 2;i++){
            temp.push_back(i);
            sum += i;
            backtracking(n,sum,temp,result);
            sum -= i;
            temp.pop_back();
        }
        return;
    }
    int climbStairs(int n) {
        int result = 0;
        vector<int> temp;
        int sum = 0;
        backtracking(n,sum,temp,result);
        return result;
    }
};

“超出时间限制”错误分析

这和算法的性能相关,有以下参考链接讲解了时间复杂度和空间复杂度。

  1. 第一篇:什么是时间复杂度?内容总结:
    在这里插入图片描述
  2. 算法为什么会超时?内容总结:
    认为力扣判断超时是因为计算超过了1s。给出如何测试O(n)、O(n^2)、O(nlogn)的1s计算规模。
    • 个人pc,执行O(n)算法,1s内处理n=8*108在这里插入图片描述
    • 执行O(n^2)算法,1s内处理n=27500.在这里插入图片描述
    • 执行O(nlogn)算法,1s内处理n=2.8*107在这里插入图片描述
  3. 递归算法求斐波那契数列算法复杂度分析内容总结:
  • 递归算法的时间复杂度是:递归的次数*每次递归的时间复杂度
  • 斐波那契数列每次递归的时间复杂度是O(1),常数级别。因为操作单元就是return n。或者深入递归。所以接下来,需要递归多少次?
  • 举个例子:深度为k的二叉树,最多有2k-1个节点。每个节点都是一次递归。那么输入n,递归n-1层。所以最多有2(n-1)-1个节点,所以需要递归2(n-1)-1次。
    在这里插入图片描述
  • 递归求斐波那契数列的时间复杂度是O(2n)。这是代码无法通过的根本原因。在本地环境测试下时间:发现随着n的增加,执行时间的增长速度符合指数级别。在n=43时,超过1s。同理,回溯的逻辑也没有问题,但是时间复杂度同理。
    在这里插入图片描述
  • 如何改进递归算法?减少递归的调用次数。
    递到下一层,修改的是first和second两个参数。在传递的过程中,就已经开始求和计算初始输入(1,2,n);
    class Solution {
    public:
        int climbStairs(int first,int second,int n) {//first放climbStairs(n-2)的值,second放climbStairs(n-1)的值
            //终止条件
            if(n==1 || n == 2) return n;
            else if(n ==3) return first+second;
            else {
                return climbStairs(second,first+second,n-1);
            }
        }
    };
    
    该代码求时间复杂度:对输入n,递归次数是n-3。当递归减到3时停止,所以操作执行次数是n-3。时间复杂度是O(n)。用这个递归测试一下时间,明显改进:
    在这里插入图片描述
  • 递归算法的空间复杂度是:递归的次数*每次递归的空间复杂度
  • 递归求斐波那契数列,输入n,递归次数是n。每次递归开辟的空间是相同的,常数级别,O(1)。所以 递归求斐波那契数列的空间复杂度是O(n)
  • 至此,可以总结:虽然求菲波那切数列有递归法(回溯法)、动态规划法。为什么参考把这道题放到动态规划章节。
    在这里插入图片描述
  1. 第四篇:什么是空间复杂度?内容总结:
    在这里插入图片描述
  2. 第五篇:程序执行需要消耗多少内存? 和语言的内存管理相关。
    在这里插入图片描述

三、参考学习

70. 爬楼梯 参考学习链接

学习内容

    1. 爬楼梯的思路和实现在分析题目中已经指出。dp[0]在参考中起始没有含义。所以我的实现中下标i和楼梯层数是错位的。注意初始化
  1. 扩展题目:70. 爬楼梯(进阶版)于下一记录发出。

总结

本文分析了爬楼梯的多种思路,对于递归法导致超时问题,在时间复杂度和空间复杂度方面进行分析。

掌握:如何推出状态转移公式?如何计算一个算法的时间复杂度和空间复杂度?

(欢迎指正,转载标明出处)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1993233.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL注入sqli-labs-master关卡三

第三关如下&#xff1a; 查看该关卡的代码发现其与关卡一和关卡二的不同之处在于id($id)这里。 那么我们输入?id1或?id2)--都能用来判断是字符型还是数字型注入。 接着输入?id1) order by 3--检查它的列数。检查到4报错&#xff0c;说明只有三列。 输入?id-1) union select…

02_快速启动 Demo 创建 Electron 项目、electron-forge 搭建一个 electron 项目、手动创建electron项目

快速启动 Demo 创建 Electron 项目 一、克隆一个仓库、快速启动一个项目二、electron-forge 搭建一个 electron 项目三、手动搭建一个 electron 项目四、开发工具中配置 Eslint 一、克隆一个仓库、快速启动一个项目 要使用 git 的话首先电脑上面需要安装 git //克隆示例项目的…

Cpp中的this指针--复习记录

1.什么是this指针? 每个类都有一个this指针&#xff0c;我们的非静态成员函数可以通过这个this指针来操作对象的成员属性。this指针存储的就是类的实例的地址&#xff0c;this指针时时刻刻指向的都是这个实例对象本身。 由下图可知: 我在主函数中栈上创建了一个类的实例(由操…

数据规模介绍

batch_size 2 1829*2 3658张图片 FSC147数据集介绍 train 3659 val 1286 test 1190

xxl-job 源码梳理(2)-服务端

目录 1. 控制面的接口2.手动触发任务2. 定时任务的实现 1. 控制面的接口 服务端包含xxl-job的管理端&#xff0c;页面上的接口后端一系列的controller接口 appName是一个核心概念&#xff0c;它是指执行器应用的名称&#xff0c;appName是执行器的唯一标识 页面上的接口&#…

出行365:依托分布式数据库,让出行无忧 | OceanBase案例

*本文首发自“新华社环球”杂志&#xff0c;作者张海鑫 每年的暑期旅游旺季&#xff0c;都会触发一轮轮的文旅消费的热潮&#xff0c;对于互联网出行服务行业而言&#xff0c;这既是一场盛大的狂欢&#xff0c;也是对其综合实力的严峻考验。 然而&#xff0c;自去年暑假起&…

Email发送接口安全性保障策略?如何优化?

Email发送接口的高级功能&#xff1f;怎么有效利用邮件API接口&#xff1f; Email发送接口的安全性对于防止数据泄露、滥发垃圾邮件和恶意攻击至关重要。AokSend将探讨Email发送接口的安全性保障策略&#xff0c;帮助开发者和企业确保其电子邮件通信的安全性和可靠性。 Email…

智能猫砂盆买错有什么危害?深度解析三款热门爆款产品!

作为一名家里还有小猫在等待的上班族&#xff0c;我们经常因为需要加班或频繁出差而忙碌得不可开交&#xff0c;导致我们很容易忽略猫咪的厕所环境和健康安全&#xff0c;每次急匆匆地出门&#xff0c;都发现自己似乎忘了给猫咪及时铲屎。但是大家要知道&#xff0c;不及时清理…

为人处世,“会说话”是一生的修行

职场上&#xff0c;常常存在这样一种现象&#xff1a;“会干活的&#xff0c;不如会说的。” 学会“好好说话”、“说正确的话”“说让人舒服的话”成为一生必须要面对的修行。 01 丰厚的学养&#xff0c;是“会说话”的根基。 同一句话&#xff0c;“会说话”的人&#xf…

XXXForm组件

效果展示 代码 XXXForm <template><div class"search-container"><el-form ref"formRef" class"form_is_hidden" :model"form" v-bind"formAttrs"><el-row :gutter"20" class"search…

一文带你快速了解——LVS负载均衡集群

前言&#xff1a; Internet的飞速发展给网络带宽和服务器带来巨大的挑战。从网络技术的发展来看&#xff0c;网络带宽的增长远高于处理器速度和内存访问速度的增长。对用硬件和软件方法实现高可伸缩、高可用网络服务的需求不断增长。针对高可伸缩、高可用网络服务的需求&#x…

.NET8使用VS2022打包Docker镜像

NET8使用VS2022打包Docker镜像 1. 项目中添加Docker支持文件2. 自定义镜像名称3. 发布Docker镜像3.1 安装Docker3.2 控制台切换到项目根目录,执行以下命令发布镜像 3.3 修改镜像名称4. 保存镜像到本地 1. 项目中添加Docker支持文件 2. 自定义镜像名称 项目文件PropertyGroup节…

软件功能测试步骤介绍,软件测试服务公司推荐

在当今软件开发日益复杂的环境中&#xff0c;软件功能测试显得尤为重要。功能测试是确保软件产品满足用户需求和规范要求的关键环节。它通过验证软件功能是否按预期运行&#xff0c;帮助发现潜在的问题&#xff0c;防止软件在上线后导致用户的不满及业务损失。随着市场竞争的加…

(el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程

Ⅰ、Element-plus 提供的DatePicker日期选择器组件与想要目标情况的对比&#xff1a; 1、Element-plus 提供DatePicker组件情况&#xff1a; 其一、Element-ui 自提供的DatePicker代码情况为(示例的代码)&#xff1a; // Element-plus 提供的组件代码: <template><e…

【多线程-从零开始-捌】代码案例2—阻塞队列

什么是阻塞队列 阻塞队里是在普通的队列&#xff08;先进先出队列&#xff09;基础上&#xff0c;做出了扩充 线程安全 标准库中原有的队列 Queue 和其子类&#xff0c;默认都是线程不安全的 具有阻塞特性 如果队列为空&#xff0c;进行出队列操作&#xff0c;此时就会出现阻…

Java代码生成器EasyCode

Java代码生成器EasyCode 一、安装插件二、连接数据库后右键Generator生成代码 一、安装插件 在 IntelliJ IDEA 的插件市场中搜索 EasyCode&#xff0c;然后安装该插件 二、连接数据库后右键Generator生成代码 勇敢面对挑战&#xff0c;成功从不会远离坚持者。坚持不懈的努力…

八股之Java集合

Java 集合&#xff0c;也叫作容器&#xff0c;主要是由两大接口派生而来&#xff1a;一个是 Collection接口&#xff0c;主要用于存放单一元素&#xff1b;另一个是 Map 接口&#xff0c;主要用于存放键值对。对于Collection 接口&#xff0c;下面又有三个主要的子接口&#xf…

MongoDB学习笔记(三)

使用Python操作MongoDB: 使用管理员用户&#xff1a;

Python —— 基础

目录 变量与引用 数据类型 赋值、深浅拷贝 控制流结构 逻辑操作符 is 与 dir() 关键字&#xff08;Python 3.11 &#xff09; https://www.cnblogs.com/qianfanwaer/p/14783204.html 变量与引用 变量是原来存储数据的一个标识符&#xff0c;可被看作是内存的一个位置&…

【学习笔记】Day 7

一、进度概述 1、DL-FWI基础入门培训笔记 2、inversionnet_train 试运行——未成功 二、详情 1、InversionNet: 深度学习实现的反演 InversionNet构建了一个具有编码器-解码器结构的卷积神经网络&#xff0c;以模拟地震数据与地下速度结构的对应关系。 &#xff08;一…