17 . 爬楼梯

题目介绍

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

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

提示:

  • 1 <= n <= 45
cpp 复制代码
class Solution {
public:
    int climbStairs(int n) {
        
    }
};

原题链接: 70. 爬楼梯 - 力扣(LeetCode)


解析

1 . 本题需求很明确,但是初次接触这种类型的小伙伴知晓怎么自己推导答案,不知背后蕴藏的算法

2 . 它给的代码,参数只有 n 个阶梯,要求返回走到n个阶梯的方法

3 . 对于先走1步还是后走1步的方法,都是算作不同方法

4 . 这意味着,拿到一个要走到n级阶梯这个目标,每次出发都有两个选择:左一个是走1步,右一个是走2步

5 . 你能想到什么数据结构呢

递归------青春版

1 . 数组排除,链表也不行。都是顺序结构

2 . 非树型结构莫属,而且也只有两个分叉------摘下面具,就是最熟悉的二叉树

3 . 二叉树与递归就好比🐟和水

4 . 这棵二叉树的每个结点就是一步台阶或者两步台阶

5 . 再看递归------老两样,核心任务和递归出口

核心任务:确定当前树要完成的任务(往往结合参数和返回值设计)

递归出口:对传入的参数进行审判
核心任务

a . 现在n就是当前树,表示现在已经走到n级台阶了,因为给的参数就是n

b . 核心任务就是计算当前树(台阶数)的方法数

c . 我们从n出发,接下来要么n-2 要么n-1 ------确定了左右子树


图1 举例

1)当2为当前树,计算出走到2的方法数;当1为当前树,计算出走到1台阶的方法数

2)当3为当前数,当前树的核心任务就是计算出走到3阶梯的方法数 = 左子树方法数 + 右子树的方法数

d . 当前树往往用当前结点表示,此处亦然。用当前走到的台阶表示

e . 需要再加个变量记录当前树的方法数否?不需要,返回值为int,直接返回就行

cpp 复制代码
class Solution {
public:
    int climbStairs(int n) {
        return climbStairs(n-1)+climbStairs(n-2);// 核心任务,一步到位
    }
};

递归出口

a . 当向下走,不断走1步,或者走2步。

1)n == 0 说明这个方法可行,返回1

2)n < 0 说明这个方法不可行,返回0

注:所以说白:就是在计数整棵树有效的叶子结点。当叶子结点为0有效,向上开始返回1

当然这个认知,也是在从推导完核心任务和递归出口,自然而然得出的

完整参考代码:

cpp 复制代码
class Solution {
public:
    int climbStairs(int n) {
        if(n == 0) return 1;
        if(n < 0) return 0;
        return climbStairs(n-1)+climbStairs(n-2);
    }
};

递归------工作留痕版

1 . 《递归------青春版》的方法,清晰明了------但一旦碰上n大的数,比如:44就会超过时间限制

2 . 难道有无用功出现吗

图3 无用功

a . 比如图上,我们会两次走到1这棵树,但是在1台阶的方法数都是左0右1,如果不拦着程序递归,它就会再去计算一遍1台阶的方法数

b . 有没有方法能让当前树如果遇到重复,就立马返回它曾经记录下的方法数

c . 不妨创建个备忘录数组

memon+1 大小为n+1 。存储的元素为 对应阶梯的方法数

d . 因为备忘录数组需要初始化,那不妨把递归逻辑放在下层

cpp 复制代码
class Solution {
public:
    int _climbStairs(int n,vector<int>& memo)
    {
        
        
    }
    int climbStairs(int n) {
       vector<int> memo(n+1,-1);
       return _climbStairs(n,memo);
    }
};

注:

1 . vector<int> memo(n+1,-1);初始化出n+1个格子,下标范围为0~n

2 . 格子里都初始化为-1

3 . 于是,递归逻辑里加上一本备忘录。记得传vector<int>& ,输出型参数的好处还记得否?减少拷贝,原地修改参数

cpp 复制代码
int _climbStairs(int n,vector<int>& memo)
    {
         if(n == 0) return 1;
        if(n < 0) return 0;
        if(memo[n] != -1) return memo[n]; // 如果以前遇到过这个阶梯,掏出备忘录,直接返回当前结点的方法数
        memo[n] = _climbStairs(n-1,memo)+_climbStairs(n-2,memo);// 先更新备忘录
        return memo[n];// 再返回
    }

完整参考代码:

cpp 复制代码
class Solution {
public:
    int _climbStairs(int n,vector<int>& memo)
    {
         if(n == 0) return 1;
        if(n < 0) return 0;
        if(memo[n] != -1) return memo[n];
        memo[n] = _climbStairs(n-1,memo)+_climbStairs(n-2,memo);
        return memo[n];
    }
    int climbStairs(int n) {
       vector<int> memo(n+1,-1);
       return _climbStairs(n,memo);
    }
};

动态规划------自下而上

1 . 不要被"动态规划"唬住,先看我接下来怎么说,慢慢去感受"动态规划"

2 . 如果说递归是从第n节台阶不断往下走,每次走1步或者2步,最后遇到有效的结点才算一个方法

3 . 那么动态规划就是直接从底部出发,不断计算出 台阶2,3,4,5,6......的方法数

cpp 复制代码
class Solution {
public:
    int climbStairs(int n) {
        vector<int> dp(n+1);// dp[i]表示i台阶的总方法数
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2;i <= n;i++)
        {
            dp[i] = dp[i-1]+dp[i-2];// 这里不要迷糊,就是dp[i]就是dp[i-1]+dp[i-2]
        }
        return dp[n];
    }
};

注:

1 . dpi等于i-1台阶的方法数加上i-2的方法数

2 . 你可以在脑海里想象,我们i-1台阶的方法数就是i-1那棵树下所有的有效结点

3 . dpi-2 也是同样的道理

4 . 起点dp0 为1,不动 (这个比较特殊;dp1只有0->1一种方法

总结以及完整参考代码:

总结暂时放一下,博主赶个路😣

cpp 复制代码
class Solution {
public:
    int climbStairs(int n) {
        if(n == 0) return 1;
        if(n < 0) return 0;
        return climbStairs(n-1)+climbStairs(n-2);
    }
};
cpp 复制代码
class Solution {
public:
    int _climbStairs(int n,vector<int>& memo)
    {
         if(n == 0) return 1;
        if(n < 0) return 0;
        if(memo[n] != -1) return memo[n];
        memo[n] = _climbStairs(n-1,memo)+_climbStairs(n-2,memo);
        return memo[n];
    }
    int climbStairs(int n) {
       vector<int> memo(n+1,-1);
       return _climbStairs(n,memo);
    }
};
cpp 复制代码
class Solution {
public:
    int climbStairs(int n) {
        vector<int> dp(n+1);// dp[i]表示i台阶的总方法数
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2;i <= n;i++)
        {
            dp[i] = dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};
cpp 复制代码
class Solution {
public:
    void dfs(int n,int& way,vector<int>& memo)
    {
        if(n < 0) return;
        if(n == 0) {
            way++;
            return;
        }
        if(memo[n]!=-1)
        {
            way += memo[n];
            return;
        }
        int tmp = way;
        dfs(n-2,way,memo);
        dfs(n-1,way,memo);
        memo[n] = way - tmp;
    } 
    int climbStairs(int n) {
        int way = 0;
        vector<int> memo(n+1,-1);
        dfs(n,way,memo);
        return way;
    }
};// 此方法其实是 工作留痕版------再加了个输出型参数,记录当前台阶的方法数way。
//于是就删掉了递归的返回值,通过way进行返回

还有两篇,燥候------

相关推荐
C++ 老炮儿的技术栈5 分钟前
MFC 自定义纯色居中文字进度条控件
c语言·数据库·c++·windows·算法·c·visual studio
researcher-Jiang6 分钟前
Manacher算法学习与应用
c++·学习·算法
码完就睡10 分钟前
数据结构——串的模式匹配算法(BF算法、KMP算法)
数据结构·算法
tachibana210 分钟前
hot100 排序链表(148)
java·数据结构·算法·leetcode·链表
林泽毅17 分钟前
Qwen3.5 DPO 模型对齐实战(pytrio训练版)
人工智能·python·算法
imuliuliang24 分钟前
算法中的随机化思想及其复杂度收益评估的技术7
算法
不能跑的代码不是好代码44 分钟前
二叉树从基础概念到LeetCode实战
算法·leetcode
凯瑟琳.奥古斯特1 小时前
二分查找解力扣1011最优运载能力
开发语言·c++·算法·leetcode·职场和发展
学计算机的计算基1 小时前
二叉树算法下篇:递归核心技巧与高频面试题详解
java·笔记·算法
水龙吟啸1 小时前
华为2026.6.24机考选择题+编程题【速刷敲黑板】
人工智能·深度学习·算法·机器学习·华为