理解递归

什么是递归?

程序调用自身解决问题的编程技巧称为递归(百度百科)

递归不能称得上是一种算法,而是一种符合人解题逻辑的编程技巧。

比较经典的问题比如汉诺塔、斐波那契数、上楼梯问题等。

怎么理解递归

首先明确他和普通的函数调用没有什么不同,只是递归一般不是立刻可以得到结果的,要经历一连串的“挂起”、“入栈”、“出栈”的过程来解决问题。

看一个斐波那契数的例子

斐波那契数后一个数等于前面两个数的和。在这个数列中的数字,就被称为斐波那契数。如数列1、1、2、3、5、8、13.......

//斐波那契数递归写法 
int Fib_1(int i){
    if(i<=2)
        return 1;
    else
        return Fib_1(i-1)+Fib_1(i-2);
} 

上面代码通过一个简单的判断结果就可以求得第N个斐波那契数,但是对于新手这段代码却是不好理解的。

根据斐波那契数的逻辑规律想一个问题解法,an= a(n-1) + a(n-2);

于是就有的第5行的递归调用。我是这样理解递归的,假如我们要执行Fib_1(4)是这样的过程。

① Fib_1(4)进栈Fib_1(3) + Fib_1(2),并挂起;递归调用Fib_1(3)

② Fib_1(3)进栈Fib_1(2) + Fib_1(1),并挂起;递归调用Fib_1(2)

③ Fib_1(2)进栈,得到结果“1”,出栈。Fib_1(1)进栈,得到结果“1”。

④ 执行return Fib_1(2) + Fib_1(1) = 2; Fib_1(3)执行完出栈,

⑤ ②执行过程中的Fib_1(2)进栈,执行return 1,②过程中Fib_1(2)出栈;

⑥ 得到②过程中Fib_1(2) + Fib_1(1)结果,Fib_1(3)出栈。

⑦ 执行①中的Fib_1(2)进栈,执行return 1,①过程中Fib_1(2)出栈;

⑧ 得到①Fib_1(3) + Fib_1(2)的结果,出栈,程序结束。

上面是斐波那契数递归解法的部分过程。递归程序有两部分组成“基准情况”和“不断推进”两个程序段,通俗点说就是“递归出口”和“不断递归深入”两个名词。也就产生了入栈、挂起、出栈的情况。(挂起只是我用来加深理解想的名词,大家随意)。另一方面一次函数的调用,栈里会存储函数调用的信息,比如返回结果的地址,形式参数具体的值,当函数达到递归出口时会根据这些信息返回结果。上面就是我对递归的理解。

用递归解决实际问题。

编写递归程序时要遵循四条法则(《数据结构与算法分析—C语言版》)

(一)基准情况:必须要有递归出口

(二)不断推进:程序的设计要一步步向基准情况推进。

(三)设计法则:假设所有递归调用都能运行。

(四)合成效益法则:设计递归时避免做重复的工作。

关于㈢设计法则,我们可以通过上面冗长的递归代码分析可以看到,对于一个复杂的递归,你不可能全面的了解每一步,这就要求我们考虑好问题,想好算法,满足算法设计的五大重要特征:有限性、确定性、可行性、输入性、输出性。才能保证递归算法的正确性。

关于㈣合成效益法则,通过书上的一个图可以更好的理解

通过上面这一个图我们可以看到F3被计算了三次,F2被计算了5次,这就违反了第四条准则“合成效益”,虽然可以得到正确的结果,逻辑上也好理解,但是当运算量达到了一定程度,就会出现出栈的情况,而且它确实不能称的上是一个好的算法。

//斐波那契数循环写法 
int Fib_2(int i){
    int k=0,fib,fib1,fib2;
    for(;k<=i;k++){
        if(k<=2){
            fib=fib1=fib2=1;
        }else{
            fib2 = fib1;
            fib1 = fib;
            fib = fib1+fib2;
        }
    }
    return fib;
}

写到这里感觉我好像在抄书,我现在的角色应该是《数据结构与算法分析—C语言版》作者思想的接受者和传播者而不是创新者。

原文地址:https://www.cnblogs.com/yweihum/p/6690272.html