以前说到,递归是一种将大问题分解为小问题的解决方案。通常来讲,递归被称为函数自身的调用。这么说可能听起来很奇怪,事实上在递归中,函数确实必须调用本身。git
例如在数学中,咱们都知道“阶乘”的概念。例如5的阶乘就是5*4*3*2*1
。github
咱们能够总结出求n的阶乘的规律,即 n! = n * (n -1) !算法
这就体现了递归。你能够从中发现,咱们把求5的阶乘一步一步转化成了另一个个的小问题。数据结构
function factorial(int $n): int { if ($n = 0) { return 1; } return $n * factorial($n - 1); }
看上面的代码,咱们能够看到对于阶乘问题的解决方案咱们有一个基础的条件就是当n为0的时候,咱们返回1。若是不符合这个条件,咱们返回n
乘 factorial(n)
,这符合递归特性的第一条和第三条。咱们避免了循环调用,由于咱们把每一次的递归调用都分解成了大问题的一个小的子问题。上面的算法思想能够表达成:数据结构和算法
上面的递归代码咱们一样可使用迭代的方法实现函数
function factorial(int $n): int { $result = 1; for ($i = $n; $i > 0; $i--) { $result*= $n; } return $result; }
若是一个问题能够很容易的使用迭代来解决,咱们为什么要使用递归?优化
递归是用来处理更加复杂的问题的,不是全部的问题均可以简单的使用迭代来解决的。递归使用函数调用来管理调用栈,因此相比于迭代递归会使用更多和时间以及内存。此外,在迭代中,咱们每一步都会有一个结果,可是在递归中咱们必须等到base case执行结束才会有任何结果。看上面的例子,咱们发如今递归算法中咱们没有任何变量或者声明来保存结果,而在迭代算法中,咱们每一次都用$result来保存了返回结果。spa
在数学中,斐波那契数列是一个特殊的整数数列,数列中的每个数的是由另外两个数求和产生的。规则以下:code
function fibonacci($n) { if ($n == 0) { return 0; } if ($n == 1) { return 1; } return fibonacci($n - 1) + fibonacci($ - 2); }
另一个使用递归算法的常见问题是求两个数的最大公因数。blog
function gcd(int $a, int $b) { if ($b == 0) { return $a; } return gcd($b, $a % $b); }
在每一次递归调用中,函数只调用本身一次,这就叫作线性递归。
在二分递归中,每一次递归调用函数调用本身两次。求解斐波那契数列的算法就是二分递归,除此以外还有二分查找、分治算法、归并排序等也使用了二分递归。
当一个递归返回的时候没有等待的操做的时候就称为尾递归。斐波那契算法中,返回值须要乘之前一个递归的返回值,所以他不是尾递归,而求解最大公因式的算法是尾递归。尾递归是线性递归的一种形式。
例如在每一次递归调用中 有 A() 调用 B(), B() 调用 A() ,这样的递归就叫作相互递归。
当一个递归函数把本身做为一个参数进行递归调用时,就叫作嵌套递归。一个常见的栗子就是阿克曼函数,看下面的表达。
看最后一行的,能够看到第二个参数就是递归函数本身。
下一篇内容会使用递归解决一些实际开发中会遇到的问题,例如构建N级分类、构建嵌套评论、目录文件的遍历等等。
PHP基础数据结构专题系列目录地址:地址 主要使用PHP语法总结基础的数据结构和算法。还有咱们平常PHP开发中容易忽略的基础知识和现代PHP开发中关于规范、部署、优化的一些实战性建议,同时还有对Javascript语言特色的深刻研究。