递归函数详解

时间:2022-01-23 02:26:43

递归函数是一个直接或间接调用函数自身的嵌套型函数。每一个递归函数都有递推(递的概念)和回推(归的概念)的过程。递推过程是将一个复杂的大问题一步步分解成简单的过程类似的小问题,而回推过程是在递归历史的基础上,从最后的最简单的小问题开始,结合该层上一层的输入参数与输出结果,按逻辑一步步向上恢复成最初的复杂的大问题。下面用一个阶乘的例子说明。
递归函数详解

递推过程:
(1) 执行A即初始化result=0,通过条件判断执行C,即result = 4*factorial(3);
(2)进入factorial(3),同样先执行A,同样通过条件判断执行C,即result=3*factorial(2);
(3)再进入factorial(2),执行A,然后C,即result=2*factorial(1);
(4)再进入factorial(1),执行A,然后C,即result=1:factorial(0);
(5)再进入factorial(0),先执行A,条件判断执行B,即result = 1;由于if语句后面只有return语句,则执行return语句,即return 1。
至此,因为factorial函数不能再次调用自己了,说明递推部分结束了。由此可见,递推部分是一个分解问题的过程,每一次调用自身,都会重新初始化,重新为形参n开辟内存空间。而整个问题按逻辑和先后顺序被分解成result = 4*3*2*1*1,其中最后的1是factorial(0)返回的值。由于使用了return语句,表示最后一个递推函数已经执行完毕,下面开始一步步往上回推。
回推过程:
(a)从递推部分的(5)开始,由于return1,将返回值带入(4),返回主调函数factorial(1),即factorial(1)=1*factorial(0)=1*1=1,由于C语句后面只有return语句,则return 1;
(b)将(4)返回的值带入(3),返回主调函数factorial(2),即factorial(2) = 2*factorial(1)=2*1=2,然后同理执行return 2;
(c)将(3)返回的值带入(2),返回主调函数factorial(3),即factorial(3)=3*factorial(2)=3*2=6,然后return 6;
(d)将(2)的返回值带入(1),返回主调函数factorial(4),即factorial(4)=4*factorial(3)=4*6=24,然后return 24。
至此,已经满足了main函数中输出factorial(4)的要求,且已经到了回推的最顶层,回推结束,返回主调函数main()。从上述描述来看,回推是一个不断返回主调函数的过程,先解决最底层Level d的函数,然后再利用该层的输出值result(不一定是return值,任何在函数运行中被调用到的处于最后状态的值都算是输出值),返回到其主调函数level c中。需替换,要注意的是,在level c函数中,除了调用自身的部分可以用level d的输出值result外,其余的变量还都是level c以前的变量和值。依次类推,不断的返回到上一层主调函数,直到最后的main函数。按照逻辑,回推过程是通过不断解决一个大问题的各个小问题来达到解决大问题的目的,其解决问题的先后顺序为factorial(4) = 1*1*2*3*4,第一个1是factorial(0)的返回值。
按照上面的理论,递推相当于一个不断输入的过程,而回推相当一个反向输出的过程,这样,一个递归函数就是将最先输入的问题最后输出,第2先输入的问题第2后输出,是不是像栈呢?没错,递归函数就是在栈空间内完成的,所以遵循先进后出的原则。每一次调用自身,都是一个输入过程,每一次调用return都是开启一个不间断的输出过程。
最后,我们来总结一下,递归函数能用于什么问题?(1)能被分解成一个或多个子问题,子问题与原问题有相同处理方法;(2)有终止递归的条件,能够确保递推和回推部分都能被终止。在main中调用factorial时,n=4是整个递推的开始条件和回推的结束条件,而n=0是整个递推的结束条件和回推的开始条件。factorial函数只有一条调用自身的语句,称为只有一条递归路线,按照问题需要,可以有多条调用自身的语句,称为多条递归路线。
虽然递归函数的代码量很短,但是由于每一次调用自身都需要为形参开辟新的内存空间,在代码量很大的情况下是不推荐使用的,会造成内存的短缺。而且,系统会保存每一次函数的状态,运行效率也不高。这时候,用循环操作来解决更合适。