递归
递归:就是在函数运行过程中调用自己。
简单的说,递归就像是平时查字典,当你遇到了一个问题 “词A是什么意思?”,你去查了字典,但是你发现字典的解释中有一个词B你不理解,因此你又一次拿起字典查词B,终于在多次查词之后,你没有再碰到不理解的词,于是你倒回去理解了所有词的意思,并解决了一开始的问题“A 是是什么意思?”
在这个过程中,字典就像是递归函数,递归终止条件就是没有再遇到不理解的词。
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
递归的要素
从上面的例子可以看出,递归有两个要素:
- 找到重复的逻辑(不认识的词),不断缩小问题规模。
- 明确递归终止的条件(没有不认识的词)。
递归存在两个过程:前行阶段和回退阶段。
- 在前行阶段,调用递归函数,会形成一个栈。
- 在回退阶段,会根据前行阶段形成的栈进行出栈。
利用斐波那契理解递归
function factorial(number)
{
if (number == 1)
{
return number;
}
else
{
return number * factorial(number-1);
}
}
factorial(5) //120
上面代码的两个要素分别是:
- 终止条件:
if (number == 1)
{
return number;
}- 重复的逻辑:
number * factorial(number-1);
接下来我们分析一下,递归解决斐波那契函数的具体过程:
-
前行过程:
在终止条件处,程序知道了 factorial(1) = 1,并不再继续调用自身,于是前行阶段结束,进入回退阶段。 -
回退阶段:
在回退阶段,程序会执行在前行阶段没有执行的代码number * factorial(number-1);
利用二叉树的遍历理解递归
假设我们需要对如下二叉搜索树进行中序遍历:
二叉搜索树中序遍历部分代码如下:
完整代码
this.inOrder = function(curNode)
{
if(curNode !== null)
{
this.inOrder(curNode.left);
console.log(curNode.val);
this.inOrder(curNode.right);
}
}
在递归过程中,程序会把访问过的结点压入堆栈,如:this.inOrder(curNode.left);
会把curNode的左孩子压入栈。
因为我们调用中序遍历时首先是tree.inOrder(tree.root)
,因此根结点入栈。
接着在函数中自己调用自己this.inOrder(curNode.left);
,左子树的根结点依次入栈。
在this.inOrder(curNode.left);
的前行阶段结束时(结束条件是curNode.left === null
),会形成以下栈:
接下来前行阶段结束,继续执行其他代码,首先打印出结点3,之后执行this.inOrder(3.right);
但是因为结点3没有右子树,因此直接碰到终止条件(this.inOrder(3.right);
访问了null结点,因此按照访问的结点都要入栈的规则,把null结点入栈,但因为null结点碰到了终止条件,因此不再调用递归,因此很快执行完成,执行完成之后把null结点出栈),因此继续执行其他代码,发现结点3的函数执行完成,把结点3弹出栈。
接下来,对结点16继续执行代码console.log(curNode.val);
因此打印出结点16,接下来继续执行this.inOrder(16.right);
,因为这个也是一个递归,因此会形成一个新的栈。
当执行到this.inOrder(19.left);
时,因为结点19没有左子树,因此碰到终止条件,开始进入回退阶段,继续执行结点19的代码,打印出结点19,并检查this.inOrder(19.right);
,因为结点19没有右子树,因此结点19的代码执行完成,把结点19出栈。
接下来执行结点22的代码,打印出结点22之后再执行this.inOrder(22.right);
,因为结点22的右子树不为null,因此又一次调用自身,形成另一个栈:
接下来执行this.inOrder(24.left);,因为没有左子树,所以继续执行console.log(24.val);
打印出结点24,之后接着执行this.inOrder(24.right);
,因为没有右子树,所以结点24的代码执行完成,把24出栈,这时栈空了,说明结点22的右子树遍历完成,回到结点22的代码,因为结点22的代码执行完成,把结点22出栈,这时栈空了,说明结点16的右子树遍历完成,回到结点16的代码。(之后的过程也和上面差不多)