图解递归,很好懂!

76 阅读2分钟

前言

“要理解递归,首先要理解递归。” ——佚名

初学递归时,它困扰了我挺长一段时间。一行调用代码就让函数一直调用直到“尽头”,计算机算得明明白白我却稀里糊涂,它究竟是怎么做到的呢,我决定来图解一番。

一、示例为先

我们先从简单的“斐波那契数列”入手。

image-20240902114758151.png

实现可爱的斐波那契数列只需要动人的两行代码:

    function fibonacci(n) {
        if (n <= 1) return n;    
        return fibonacci(n - 1) + fibonacci(n - 2);  
    }

我们以fibonacci(4)为例来看看它的执行过程是怎么样的(以下fibonacci(n)简写为f(n)):

image-20240902140905410.png

image-20240902140953954.png

image-20240902141449029.png

image-20240902142003753.png

image-20240902143041287.png

image-20240902143151580.png image-20240902143731358.png 看完是否脑海中对递归更加有画面感了?

如果我们把这个调用过程画成一棵树,可以清晰的看到调用的层级关系:

image-20240902144241344.png

可以看到,树的每个叶子都有确切值,这是因为有基线条件的设置(if(n <= 1) return n;)。

二、总结

通过上面图解,我们可以了解到递归有以下特点:

  1. 必须要有基线条件,即使得函数终止的条件,如果没有这个条件,将会陷入无限调用;
  2. 函数调用会在内存中生成"调用帧",用来保存调用位置内部变量等信息。每调用一个函数就生成一个"调用帧",后调用的函数调用帧在前函数之上。所有的调用记录形成一个"调用栈"(call stack),也就是图解部分所示的“栈”。
  3. 递归通常使用分治思想,将问题分解为更小、更易解决的子问题。这些子问题的解最终合成原问题的解。结合上面的树形图可以加深对这句话的理解。

如果只是简单理解递归,到这里应该差不多。但是递归结合数据结构和算法还能跟”二叉树“和”动态规划“等许多知识挂钩。我会在后面的文章更新这部分知识。

如有错漏之处还请各位指正!如有帮助不妨动动手指头点个赞再走~❤