算法:关于递归(PHP)

目录

一、什么是递归

二、递归的基本原理

三、递归的优缺点

四、体会:一个累加递归函数

五、总结一下:

1.普通的递归斐波那契数列

2.尾递归:在使用递归的情况下,不爆栈

3.记忆化搜索:减少不必要的重复计算,自上而下

4.动态规划:自上而下,非递归



一、什么是递归

递归,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。也就是说,递归算法是一种直接或者间接调用自身函数或者方法的算法。

通俗来说,递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。

二、递归的基本原理

第一:每一级的函数调用都有自己的变量。

第二:每一次函数调用都会有一次返回。

第三:递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序。

第四:递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反。

第五:虽然每一级递归都有自己的变量,但是函数代码并不会得到复制。

三、递归的优缺点

1、优点:实现简单,可读性好。

2、缺点:递归调用,占用空间大;递归太深,易发生栈溢出;可能存在重复计算。

四、体会:一个累加递归函数

1、要求:求某个数的累加值。如add(5),求1-5的累加值

2、分析:1-5的累加值,可以分解为:

(1)add(5)  = 5+add(4);     //则计算公式为:$sum = $i + add($i -1)

(2)add(4) = 4+add(3);

(3)add(3) = 3+add(2);

(4)add(2) = 2+add(1);

(5)add(1) = 1+add(0);   //终止条件  $>0

从上述分析可知,可以累加问题可以分解,并且解决思路一致。

3、程序代码如下:

function add($i)
	{
		if ($i > 0) {                  //终止条件
			$sum = $i + add($i - 1);   //调用递归函数
			return $sum;
		}
	}
	
	$result = add(5);

4、为体会调用过程,可用此程序体会:

<?php
	/**
	 * 递归函数要点
	 *  1、一个问题可以分解为几个子问题的解。
	*   2、这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一致。
	*   3、存在递归终止条件。
	 */
	function add($i)
	{
		if ($i > 0) {//终止条件
			echo '递归前:$i=' . $i . "<br>";
			$sum = $i + add($i - 1);   //调用递归函数
			echo '<hr>';
			echo '递归后:$i=' . $i . "<br>";
			echo '递归后:$sum=' . $sum . "<br>";
			return $sum;     //此return语句可以去掉,看一下效果
			/**如果递归函数过程涉及某种计算,
			 * 则需加return返回计算的结果,
			 * 否则每层递归返回数据。
			 * 如只是输出显示数据,则可不需要return证句
			 */
		}
	}
	$result =add(5);

运行结果如下:

五、总结一下:

通过学习和动手实践,对递归函数有了初步的认识,但尚在初浅阶段。

递归函数:

(1)函数体内调用函数本身,这说明函数完成的功能是一致的,区别是处理的数据不同而已;

(2)return语句可以不用,一般用在函数计算结果需要返回时。如果无结果返回,只是递归显示数据则可以不需要return语句。

(3)对递归的过程:

执行到递归函数调用的时候,则进入:递归体1->递归体2->递归体3->递归体4->……->递归体n,

直到递归条件终止时,逐层返回:递归体n->递归体n-1->递归体n-2->递归体n-3->……->递归体1,

再来看看这张图片,可能更好理解一点。

(4)通过递归的三大要素来深入理解递归函数的应用

  • 第一要素:明确你这个函数想要干什么。先不管函数里面的代码什么,而是要先明白,你这个函数的功能是什么,要完成什么样的一件事。
  • 第二要素:寻找递归结束条件。我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。
  • 第三要素:找出函数的等价关系式。我们要不断缩小参数的范围,缩小之后,我们可以通过一些辅助的变量或者操作,使原函数的结果不变。

关于递归的优化

1、考虑是否重复计算

如果你使用递归的时候不进行优化,是有非常非常非常多的子问题被重复计算的。因此,使用递归的时候,必要须要考虑有没有重复计算,如果重复计算了,一定要把计算过的状态保存起来。

2、考虑尾递归

对于递归的问题,我们一般都是从上往下递归的,直到递归到最底,再一层一层着把值返回。

不过,有时候当 n 比较大的时候,例如当 n = 10000 时,那么必须要往下递归10000层直到 n <=1 才将结果慢慢返回,如果n太大的话,可能栈空间会不够用。这个时候,就可以用尾递归优化来解决。

顾名思义,尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量。直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去。

案例:

求斐波那契数列递归算法

<?php
	function fib($num)
	{
		if ($num == 0) {
//			echo $num . '->';
			return 0;
		} elseif ($num == 1) {
//			echo $num . '->';
			return 1;
		} else {
			$list = fib($num - 1) + fib($num - 2);
			return $list;
		}
	}
	
	echo '<pre>';
	for ($i = 0; $i < 10; $i++) {
		echo fib($i).'->';
	}
	echo '</pre>';

1.普通的递归斐波那契数列


function fac($n){
    if($n == 1 || $n == 2) return 1;
    else return fac($n-2) + fac($n-1)

2.尾递归:在使用递归的情况下,不爆栈

普通的递归,运行栈会被函数的递归调用占满了

因此在要求使用递归的情况下,可以使用尾递归

每次调用函数时不生成新的运行栈,利用上一次的结果 

/**
 * $a充当收集器,收集上一次运行栈的返回值,之后栈空间会被回收
 * $a和$b参与每次的计算
 * $n是斐波那契数列执行的次数
 */
function fac1($a,$b,$n){
    if($n>2) return fac1($a+$b,$a,$n-1);
    return $a;
}

3.记忆化搜索:减少不必要的重复计算,自上而下

在原本的斐波那契递归中,总会像如下图一样递归到最后再返回结果

其中,例如 以5为例,3 往下的部分,就会被重复计算两次,2往下的部分会被重复计算3次

如果数据量大的情况下,记忆化搜索减少的计算量是十分可观的

//用该方法可以大大优化斐波那契数列解决速度 

class Solution{
    private $memory = [];            //记录当前数字是否已经求出解
    public function fac($n){
        if($n == 0) return 0;
        if($n == 1) return 1;
        if(empty($this->memory[$n])) //若没有对应的解,则继续进行递归
            $this->memory[$n] = $this->fac($n-1) + $this->fac($n-2);
        return $this->memory[$n];    //有解则直接返回该斐波那契数列
    }
}
$q = new Solution();

————————————————
版权声明:本文为CSDN博主「YY-帆S」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010365335/article/details/86408252

4.动态规划:自上而下,非递归

非递归,减少了系统栈的建立,比记忆化搜索还快

将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案

function fac($n){
    if($n == 1 || $n == 2) return 1;
    $a = 1;
    $b = 1;
    $res = 0;
    for($i = 3;$i<=$n;++$i){
        $res = $a + $b;
        $a = $b;
        $b = $res;
    }
    return $res;
}

PHP里的尾递归及其优化?点击查看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值