【算法】递归算法 ① ( 使用递归推导斐波那契数列 | 递归内存开销分析 | 递归三要素 : 定义 拆解 出口 )





一、使用递归推导斐波那契数列



斐波那契数列 : https://leetcode.cn/problems/fei-bo-na-qi-shu-lie-lcof/

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。


1、问题分析

斐波那契数列分析 :

斐波那契数列 的 第 n 项 F(N) 依赖于 其第 n - 1 项 和 n - 2 项 相加的值 F(N - 1) + F(N - 2) ;

该算法 可以使用 递归 进行解决 ;


2、递归特点

递归特点 :

递归就是 函数 自己 调用 自己 , 那么在 递归函数 中 , 需要一些参数变化 , 否则会一直不停的循环递归下去 ;

扫描二维码关注公众号,回复: 14554574 查看本文章

递归操作 类似于 给 洋葱剥皮 , 每次递归调用之后 , 整个问题的规模一直不断的变小 , 直到达到递归停止条件为止 ;


3、递归内存开销

递归的内存开销分析 : 函数执行时 , 需要在栈内存 中 存储当前函数的

  • 函数参数列表
  • 函数返回值
  • 函数局部变量

每次递归都要 在 栈内存 中开辟 内存空间 存储本次调用函数的 参数列表 , 返回值 , 局部变量 , 如 : 函数参数是一个 int 类型值 , 则每递归一层 , 就要消耗 4 字节栈内存空间 ;

递归操作 每次调用 函数本身 , 都需要 在 栈内存 中 保存当前的调用细节 , 因此 递归操作 要消耗栈内存空间 , 如果递归的调用深度比较深 , 达到几十万次 , 有可能导致 栈内存溢出 StackOverflow ;

由于 递归 会消耗大量的栈内存空间 , 递归操作 能不用就不用 ;


递归第一层的 n , 与下面一层的 n 是不一样的 , 以 f(n) = f(n - 1) + 2 , f(1) = 1 为例进行递归调用为例 ,

  • 第一层调用的参数是 n , 栈内存空间保存该层调用数据 ,
    • 第二层调用递归时 , 将 n - 1 作为 n 传递给了函数作为参数 , 栈内存空间保存该层调用数据 ,
      • 直到调用到最底层 , f(1) = 1 , 开始逐层进行返回 , 每返回一层就会释放一层栈内存空间 ;

4、递归三要素

递归三要素 :

  • 递归定义 : 分析函数的 参数 , 返回值 , 以及代表的含义 ;
  • 递归拆解 : 分析每次 递归需要执行的操作 , 就是递归函数的具体内容 ;
  • 递归出口 : 每个递归都需要一个 停止条件 , 递归不断循环会造成栈内存溢出 ;

5、代码示例

class Solution {
    
    
	// 1. 递归定义 : 传入的 n 的含义是斐波那契数列的索引值, 
	// 返回值的含义是斐波那契数列的索引值对应的元素值
    public int fib(int n) {
    
    
    	// 3. 递归出口 : 当递归执行到 n = 0 时, 开始逐层向上返回 
        if(n <= 1) {
    
    
            return n;
        }
        // 2. 递归拆解 : n 索引的元素值 是 n - 1 和 n - 2 索引元素之和
        return fib(n - 1) + fib(n - 2);
    }
}

在 LeetCode 中不能使用递归推导 斐波那契数列 , 时间复杂度是 O ( n 2 ) O(n^2) O(n2) , 达不到要求 ;

猜你喜欢

转载自blog.csdn.net/han1202012/article/details/127719504