文章转载链接1:https://blog.csdn.net/ohmygirl/article/details/19679643
文章转载链接2:https://blog.csdn.net/wzqnls/article/details/38705143
递归的定义
递归(http:/en.wikipedia.org/wiki/Recursive)是一种函数调用自身(直接或间接)的一种机制,这种强大的思想可以把某些复杂的概念变得极为简单。在计算机科学之外,尤其是在数学中,递归的概念屡见不鲜。例如:最常用于递归讲解的斐波那契数列便是一个极为典型的例子,而其他的例如阶层(n!)也可以转化为递归的定义(n! = n*(n-1)!).即使是在现实生活中,递归的思想也是随处可见:例如,由于学业问题你需要校长盖章,然而校长却说“只有教导主任盖章了我才会盖章”,当你找到教导主任,教导主任又说:“只有系主任盖章了我才会盖章”...直到你最终找到班主任,在得到班主任豪爽的盖章之后,你要依次返回到系主任、教导主任、最后得到校长的盖章,过程如下:
以上事例体现了递归的基本思想,也就是递归的两个基本条件:
-
递归的退出条件,这是递归能够正常执行的必要条件,也是保证递归能够正确返回的必要条件。如果缺乏这个条件,递归就会无限进行下去,直到系统给予的资源耗尽(在大多数语言中,都是堆栈空间耗尽),因此,如果你在编程中碰到类似“stack overflow”(C语言中,即栈溢出)和“max nest level of 100 reached”(php中,超出递归限制)等错误,多半是没有正确的退出条件,导致了递归深度过大或者无限递归。
-
递推过程。由一层函数调用进入下一层函数调用的递推。以n!为例。在n>1的情况下。N! = N*(N-1)! 便是该递归函数的递推过程,我们也可以简单的称为“递归公式”
有了这两个基本条件,我们便得到了递归的一般模式, 用代码可以描述为:
function Recur( param ){
if( reach the baseCondition ){
Calu();//计算
return ;
}
//else just do it recursively
param = modify(param)/修改参数,准备进入下层调用
Recur(param);
}
有了递归的一般模式,我们便可以轻松实现大多的递归函数。例如:经常提起的斐波那契数列的递归实现,再如,目录的递归访问
还有 递归使用 “层”这个概念。主要有两大原因:
1. 人们在分析递归的过程中,经常使用递归树的形式来分析递归函数的走向。以斐波那契数列为例,首先斐波那契数列的定义为:
因此,为了得到Fab(n)的值,我们常常需要展开为“递归树”的形式,如下图所示:
而递归的计算过程则是从上而下,从左而右,一旦到达递归树的叶子节点(也就是递归的退出条件),便又层层向上返回。
作为递归的基本实例,以下可用于练习:
1. 目录的递归遍历。
2. 无限分类。
3. 二分查找和合并排序。
4. PHP内置的与递归行为有关的函数(如array_merge_recursive,array_walk_recursive,array_replace_recursive等,考虑它们的实现)
理解递归-函数调用的堆栈跟踪
在php中,可以使用的调试方法有:
1.原生的print ,echo ,var_dump,print_r等,通常对于较为简单的程序,只需要在函数的 关键点输出即可。
2.Php内置的堆栈跟踪函数:debug_backtrace 和debug_print_backtrace.
3.xdebug 和xhprof等调试工具。
使用范例:
function fab($n){
debug_print_backtrace();
if($n == 1 || $n == 0){
return $n;
}
return fab($n - 1) + fab($n - 2);
}
fab(4);
还有一个形象的解释:
代码A:
<?php
//递归函数
$num=10;
function add($sum){
static $tot;
if($sum>=1){
$tot+=$sum;
add(--$sum);
}else{
return $tot;
}
}
echo add($num);
?>
代码B:
<?php
//递归函数
$num=10;
function add($sum){
static $tot;
if($sum>=1){
$tot+=$sum;
return add(--$sum);
}else{
return $tot;
}
}
echo add($num);
?>
A中打印不出想要的结果,B中可以实现。而A和B代码中的唯一差别就是在if中添加了一个return。
下面就开始分析整个递归的过程,完全可以去看看盗梦空间加深理解了:(以不能实现的A代码为例)
1.将10带入函数中,if语句判断之后,$tot开始叠加。
2.这里重点来了:将参数减一之后重新带入函数中。(没有返回值!!!)
3.接着上面的过程一直循环,一直到$sum=1,这从外到内,由10到1,这10层的循环都没有返回值。
4.$sum=0的时候,要求有返回值。
就好像,盗梦空间中,主角在11层梦境中醒来,但是前面10层的梦境都处于睡眠阶段。这主角你觉得醒的过来吗,明显不可能。他只能卡在第11层梦境,永远醒不过来。
唯一能让主角醒过来的办法也就是他的每一层梦醒都醒过来,一直回到现实生活中。而相对与A代码,B代码则从第一层开始就要求有返回值,一直到11层都要有返回值。所以11层醒过来之后激活了第10层,然后一层一层的激活,最后就顺利醒过来了,这个最后的结果也就正确输出了。