首先上两个例子
//菲波那切定理的迭代和递归实现
//递归实现:
int fib1(int n) {
if (n <= 1) return 1; //递归中止判断和求到值了
return fib1(n-1) + fib1(n-2);
}
//迭代实现
int fib2(int n) {
int f0 = 1, f1 = 1, i; //将f1,f2作为全局游标,用于迭代
for (i = 2; i <= n; ++i) {
int f2 = f1 + f0;
f0 = f1;
f1 = f2;
}
return f1;
}
//最大公约数的迭代和递归
//递归问题
int max_common_digui(int x, int y)
{
if(x%y==0){
return y;
}
else{
return max_common_digui(y,x%y);
}
}
//迭代解决
int max_common_diedai(x,y){
while((x%y)!=0){
y=x%y
x=y
if(x%y==0){
return y
}
}
}
林锐博士的高质量C++对递归进行了很精辟的讲解,下面是我自己对他讲解的一种归纳和理解。
递归是一个逐步分解的过程,通过函数调用自己,将问题逐步分解为多个自己去计算(逐个压栈),然后所有结果达到基本条件后再返回值,并销毁的过程(逐个出栈)这里面有两个思想:一个是逐步分解,将复杂问题(这个复杂主要体现在规模上)化为单个问题去解决。另一个是入栈出栈的问题,这个是内存分配和存储的问题。
递归有两个条件
1 能够递归,也就是能够通过不断调用自己完成任务的分解,我觉得其实就是可分解性。
2 递归停止的条件。就是递归不断分解后直到能够解决的那个简单问题,在这个时候就能够获取真正有价值的数据结果了。然后用这个结果一层层再返回过去,使得初始的问题得到解决。
递归实现的机理保证
(1)内存上的保证函数的堆栈可以根据函数运行的需求自动增长,直到能够提供的最大内存都耗干了。(如果不能动态增加,那么自己不断调用自己,哪里来那么多堆栈分配存储空间)
(2) 结构上的保证递归从逻辑上就是压栈和出栈。 但凡递归没有满足基本条件,我就不断的递归下去,不断开辟存储空间,调用新的递归函数,出栈出栈下去,每一层的递归函数都等着下一层递归的函数返回值过来,他们就一直等啊等,不会被销毁。而一旦达到基本条件,整个程序就会从最末端的递归子程序逐步返回值,同时自己也进行销毁,也就是出栈了。这样从下往上,各个递归函数就会一层一层的接到下面递归子函数传来的值,传给自己的上级,同时自己也销毁了,直到最上面一层的递归函数得到了返回值,贡献给主函数,同时自己也销毁了压栈(进栈)是个从上往下繁衍创生的过程,出栈是个自下而上完成任务然后自我销毁的过程。(突然觉得这个场景很悲壮。。。)
3递归和迭代的不同
(1)首先二者转换的关系:任何用递归实现的问题,都可以用迭代来实现。
其次递归的优势和劣势:
递归反复调用函数,生成大量堆栈空间,运行开销大。但是递归能够直观反应问题,可读性强。
迭代的优势和劣势只发生在一个函数内部,反复使用局部变量计算,开销小得多。再次,个人认为,递归是函数层面的重复调用,迭代是定义了一个全局的游标,在变量层面进行重复赋值。递归的具体实现是通过A函数中包含A函数 A{A}不断衍生和返回。而迭代的实现则是靠while循环和i=i%2这类的自我有条件的反复赋值进行的。
4 从格式和通信传递上思考
递归
1 分解就是分解,除了对入口函数参数需要修改更新之外,其他不需要什么操纵了
2 真正有数据值返回的地方就是判断停止递归的地方,这个地方真正返回实际的数据,然后整个栈开始往回收,把数据都收回来。
最经典的递归函数套路是这样的
def 递归(入口参数1,入口参数2)
if(判断条件1):
求值
return 求值结果
else:
问题分解,分解出子问题
递归(入口参数1更新,新入口参数更新);
return 递归集
递归函数的过程一定有个通信过程,
当递归的时候,从外向内消息的传递时通过入口参数渠道完成的,
而当递归到中止条件,我们求值的时候,又通过return这个渠道从内向外将值传回去。
迭代
迭代不管是while还是for循环,一个全局的游标在外面,保证迭代可以不断重复赋值,到满足条件的时候通过return把他们迭代完的值返回来。
迭代常用的格式可以是这样的
定义外部游标变量cursor
for( 事先确定的迭代次数):
1-利用cursor进行赋值,计算。
2-对cursor进行更新,直到迭代次数为止。
while( 事先确定的迭代次数):
1-利用cursor进行赋值,计算。
2-对cursor进行更新,直到迭代次数为止。
5 从内存上思考
迭代的游标使用的内存都是那一块,只不过随着迭代,值被多次更新而已,但是内存消耗是平稳的。
而递归尽管我们看到递归过程的代码都是那几个,但是每次递归都是一个新的生产的过程,这就像一个嵌套的过程,每个新的递归函数都意味着产生一个新的计算和存储资源,尽管递归名字相同,但是所用的内存块是绝对不一样的,递归之间唯一的传递取代就是入口参数而已,他们没有任何共用变量的地方存在
6 递归都是调用自己,不要使用间接递归,即一个函数通过调用另一个函数来调用自己,损害清晰性。