一分钟了解递归 leetcode 经典爬楼梯问题

一分钟了解动态规划 体验一下递归的暴力美学

哈喽哈喽,我是你们的金樽清酒,今天呢我来带大家一分钟了解透彻递归的问题,体验一下递归的暴力美学。

首先我们来看一下leetcode上的经典问题 爬楼梯

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

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= n <= 45

首先看到这道题,你会怎么去做呢?我们分析一下,一层楼有一种方法,二层楼有两种解法,三层楼三种方法,四层楼呢,我们勉强可以推算出是五种方法,但是都说是勉强了,那么再多一楼层大脑就已经反应不过来了。但是我们可以找到一个规律,从三层楼开始,三层楼的方法等于一楼加二楼的方法,四楼等于三楼加二楼的方法。诶,假设一个函数f(n),那么就有f(n)=f(n-1)+f(n-2),但是知道这个公式有什么用呢?

什么是递归呢?

首先,递归并不是一种算法,他是一种函数功能的实现。递归是一种编程技术,它通过在函数内部调用自身来解决问题。在递归过程中,问题被分解为更小的子问题,每个子问题都可以通过相同的方法来解决。递归在许多问题中非常有用,因为它能够简化问题的表达和解决过程。

递归通常包含两个部分:基本情况和递归情况。基本情况是指问题的最简单形式,它不再需要递归调用来解决。递归情况是指问题仍然需要进一步拆分为更小的子问题,并通过递归调用来解决。

当一个函数被调用时,它会将当前的执行状态保存在一个称为"栈帧"的数据结构中。每个栈帧包含了函数的局部变量、参数和返回地址。当递归调用发生时,一个新的栈帧被创建并推入调用栈中。递归的结束条件通常是在某个点上达到基本情况,这时递归调用停止,并开始将结果从底层逐步返回到顶层。

递归可以用于解决许多问题,如数学中的阶乘、斐波那契数列等。然而,递归也可能导致性能问题,因为它会导致多次重复计算。为了避免这种情况,可以使用记忆化技术或尾递归优化。

总的来说,递归是一种强大的编程技术,可以用于解决许多问题。但在使用递归时,需要小心处理基本情况和递归情况,以确保递归可以正常终止,并且要注意性能方面的考虑。

什么情况下用递归呢?

  1. 问题可以通过将其拆分为更小的子问题来解决,每个子问题可以通过相同的方法解决。
  2. 问题的结构本身是递归的,例如树形结构和图形结构等。
  3. 递归可以使代码更加简洁和易于理解,特别是在与其他递归算法相关的领域中,如函数式编程。
  4. 在某些情况下,递归可以比迭代更加高效、更加清晰,因为它可以利用调用栈来保留程序状态。

需要注意的是,递归在某些情况下可能会导致性能问题,因为它会导致多次重复计算。因此,在使用递归时,需要注意选择正确的结束条件和递归调用的顺序,以确保递归可以正常终止,并且要注意性能方面的考虑。

废话不多说,我们来看一下这一题怎么用递归去解决呢?

js 复制代码
/**
 * @param {number} n
 * @return {number}
 */

var climbStairs = function(n) {

    if(n===1)  return 1
    if(n===2)  return 2
    return  climbStairs(n-1)+climbStairs(n-2) 
};

我们来看一下为什么是这样去写呢?由上面的分析我们知道,f(n)=f(n-1)+f(n-2),那我们要返回的就是climbStairs(n-1)+climbStairs(n-2),函数是一个个的栈,因为我们不知道f(n-1)和f(n-2)是多少,所以函数本身就会不停的调用,直到找到可用值为止,那就开始'归'啦,一直'归'到我们需要的值。所以就这么简单几行代码就能实现这个功能啦,是不是很暴力!不过是不是真的这么简单呢?诶,有点小插曲。

但你提交的时候会告诉你,超出时间限制,测试用例没有完全通过。上文说过了递归也可能导致性能问题,因为它会导致多次重复计算。为了避免这种情况,可以使用记忆化技术或尾递归优化。

递归的优化 记忆化技术

在上述代码中,由于没有记忆技术,每一个f(n)的值都会重复递归好多次,重复计算,浪费性能导致超时,不过这都是小问题啦,我们用一点点记忆化技术就能解决这个问题。看下述代码。

js 复制代码
const f=[];//全局
const climbStairs=function(n){
    //退出条件
    if(n===1) return 1
    if(n===2) return 2
    if(f[n]===undefined){
        //函数嵌套函数,这就是递归
        f[n]=climbStairs(n-1)+climbStairs(n-2)
    }
    return f[n];
} 

对比第一个代码,我们的优化代码多定义了一个全局变量,然后将返回的值存在变量里面,这样就能减少好多的重复计算,优化了性能。

看,这样写我们是不是就通过了呢?

总结

递归是一种函数功能的实现,它不是一种算法。递归是一种编程技术,它通过在函数内部调用自身来解决问题。但是递归会进行大量的重复计算,影响性能,所以我们还需要一些优化手段。该说不说社会你归哥,人狠话不多,切起题来那是相当的快。

相关推荐
柯南二号10 分钟前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
究极无敌暴龙战神X27 分钟前
前端学习之ES6+
开发语言·javascript·ecmascript
明辉光焱30 分钟前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
TN_stark93233 分钟前
多进程/线程并发服务器
服务器·算法·php
汉克老师1 小时前
GESP4级考试语法知识(贪心算法(四))
开发语言·c++·算法·贪心算法·图论·1024程序员节
nameofworld1 小时前
前端面试笔试(二)
前端·javascript·面试·学习方法·数组去重
smj2302_796826522 小时前
用枚举算法解决LeetCode第3348题最小可整除数位乘积II
python·算法·leetcode
爱吃生蚝的于勒2 小时前
C语言最简单的扫雷实现(解析加原码)
c语言·开发语言·学习·计算机网络·算法·游戏程序·关卡设计
hummhumm2 小时前
第 12 章 - Go语言 方法
java·开发语言·javascript·后端·python·sql·golang
hummhumm2 小时前
第 8 章 - Go语言 数组与切片
java·开发语言·javascript·python·sql·golang·database