前言
“要理解递归,首先要理解递归。” ——佚名
初学递归时,它困扰了我挺长一段时间。一行调用代码就让函数一直调用直到“尽头”,计算机算得明明白白我却稀里糊涂,它究竟是怎么做到的呢,我决定来图解一番。
一、示例为先
我们先从简单的“斐波那契数列”入手。
实现可爱的斐波那契数列只需要动人的两行代码:
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
我们以fibonacci(4)
为例来看看它的执行过程是怎么样的(以下fibonacci(n)简写为f(n)
):
看完是否脑海中对递归更加有画面感了?
如果我们把这个调用过程画成一棵树,可以清晰的看到调用的层级关系:
可以看到,树的每个叶子都有确切值,这是因为有基线条件的设置(if(n <= 1) return n;
)。
二、总结
通过上面图解,我们可以了解到递归有以下特点:
- 必须要有基线条件,即使得函数终止的条件,如果没有这个条件,将会陷入无限调用;
- 函数调用会在内存中生成"调用帧",用来保存调用位置和内部变量等信息。每调用一个函数就生成一个"调用帧",后调用的函数调用帧在前函数之上。所有的调用记录形成一个"调用栈"(call stack),也就是图解部分所示的“栈”。
- 递归通常使用分治思想,将问题分解为更小、更易解决的子问题。这些子问题的解最终合成原问题的解。结合上面的树形图可以加深对这句话的理解。
如果只是简单理解递归,到这里应该差不多。但是递归结合数据结构和算法还能跟”二叉树“和”动态规划“等许多知识挂钩。我会在后面的文章更新这部分知识。
如有错漏之处还请各位指正!如有帮助不妨动动手指头点个赞再走~❤