对许多编程初学者来说,递归算法是语言学习的最大障碍之一。 大多数人也不懂的事情是一半,毕竟学习深层意境也是因为自己基础不好,发展太慢了。
虽然知道递归,可能大部分人都能读懂递归,但是在实际解决问题的过程中,不知道怎么使用。 今天,我们来谈谈递归算法的使用。
什么是递归
递归是指在数学和计算机科学中使用函数本身来定义函数的方法。 也就是说,递归算法是直接或间接调用自身函数或方法的算法。简单地说,递归算法的本质是将问题分解为规模缩小的同类问题的子问题,递归调用方法表示问题的解。
递归的基本原理
第一:各级函数调用都有自己的变量。第二,每次函数调用都返回。
第三,在递归函数中,递归调用前面的语句和各类被调用函数具有相同的执行顺序。
第四,在递归函数中,递归调用后的语句的执行顺序和各调用函数的顺序相反。
第五,各级递归都有自己的变量,但不复制函数代码。
递归的优缺点
的好处实现简单
易读性
缺点
递归调用、占用空间大
递归太深,容易发生堆栈溢出
可能有重复计算
递归的三大要素
第一要素:明确你这个函数想做什么。 不管函数中的代码是什么,首先要明白你函数的功能是什么,会完成什么样的事情。第二要素:寻找递归终止条件。 需要找出为什么参数要递归终止,然后直接返回结果。 请注意,此时必须直接从该参数的值中知道函数的结果是什么。
第三要素:找出函数的等价关系式。 有必要缩小参数的范围。 缩小后,可以通过一些辅助变量或操作使原始函数的结果恒定。
递归过程
具体来说,如果递归函数调用自己,则调用的函数也将调用自己,并且将无限循环,除非代码中包含终止调用链的内容。 常规方法将递归调用放入if语句中。 例如,void类型的递归函数recurs (的代码如下:
用文字再现该代码块的内容:
只要if语句为true,则调用每个recurs (调用执行statement1,然后调用recurs )。 语句2不执行。 当前调用结束后,程序的控制权将通过调用方的recurs (返回,recurs )执行statements2部分结束,并将控制权返回给上一个调用。 以下也一样。
递归的使用
递归的强大之处在于用户可以用有限的语句描述无限个对象。 因此,在计算机科学中,可以使用递归来描述无限步的运算,但是描述运算的程序有限。 这不容易循环。要创建正确的递归算法,必须执行“递归”步骤。 也就是说,递归算法必须在分解问题后出现无法进一步分解的步骤时,使递归具有结束条件。 否则,将陷入死区,最终导致内存不足导致堆栈溢出异常。
在这里,我们通过两个例子来学习递归使用。
例1 :递归地求出阶乘
例2 )递归求出斐波那契数列
从上面的步骤可以很好地看出递归算法的第一步是分割统治。 通过将复杂的大问题分割为一个个小问题,直到不能再分解为止,摆脱条件retrun,然后从最小的问题开始求解,在所有子问题都解决之前,最终的大问题都得以解决。
在
递归的优化方法
递归问题中,思考方式本身并不困难,真正的难点是如何进行优化。caption">
1、考虑是否重复计算
如果你使用递归的时候不进行优化,是有非常非常非常多的子问题被重复计算的。因此,使用递归的时候,必要须要考虑有没有重复计算,如果重复计算了,一定要把计算过的状态保存起来。
2、考虑尾递归
对于递归的问题,我们一般都是从上往下递归的,直到递归到最底,再一层一层着把值返回。
不过,有时候当 n 比较大的时候,例如当 n = 10000 时,那么必须要往下递归10000层直到 n <=1 才将结果慢慢返回,如果n太大的话,可能栈空间会不够用。这个时候,就可以用尾递归优化来解决。
无情的鲜花,尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量。直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去。
有的人刚接触算法的时候,一直都惧怕递归,也很少或者说几乎就不写递归的代码。但其实学习了以后,发现递归还是挺可爱的。就像在数学找一组数字的规律一样,可以锻炼我们的思维。希望这篇文章,能额昂你有所收获。