今天又写了个C++中关于字符串倒序输出的,要求用递归算法求解!
关于递归,以前也思考了很久自以为已经理解好了,但是今天做题还是磕磕碰碰,不能够快速的反应过来,所以觉着有必要好好总结下,写出自己认为的最好理解方式!
当然大家都知道,递归的本质和栈数据的存取很相似了,都是先进去,但是往往最后处理!再者对于递归函数的局部变量的存储是按照栈的方式去存的,对于每一层的递归函数在栈中都保存了本层函数的局部变量,一边该层递归函数结束时能够保存原来该层的数据!如图:
如上图递归式依次往下进行的,并且在该层递归函数还没结束即将进入下一层递归调用时,将会把该层函数中的局部变量保存起来,以供下次使用!
好了,以上是递归函数的数据存储方式,可是有时候我们又得抓头了,递归的话,有时候又很难理解,貌似总也想不通!
于是我又把每一层递归函数化分为三部分了,第一部分:是递归调用前的一些数据处理,判断以及递归结束判断(当然了结束条件肯定在递归调用前,要不每次递归就不会结束了),第二部分:就是递归函数本身了。而第三部分:当然就是递归函数的后续处理代码了!在这里我想我们得想明白一件事情了,每一层的函数都是在上一层递归函数结束时才返回的然后接着处理该层递归函数剩下的部分!例如如下代码:
#include<iostream>
#include<string>
using namespace std;
int i=0,j;
void reverse(string &s);
int main()
{
string s;
cin>>s;
j=i=s.size();
reverse(s);
cout<<s<<endl;
return 0;
}
void reverse(string &s)
{
char ch; //..........第一部分 ..........
i--;
ch=s[i];
cout<<ch<<endl; //这里i是全局变量,而ch是局部变量会保存在栈中
if(-1==i)
return;
reverse(s); //本身的递归看做第二部分
//后续部分看做第三部分
s[--j]=ch; //这句当且仅当该递归函数中的reverse返回时 才执行
cout<<ch<<endl;
}
在以上代码中,每一层只有当reverse()结束了才会接着处理下面的s[--j]=ch;代码,因为每一次递归进去的时候reverse()上面的代码都已经处理了,所以当递归返回时处理的自然就是reverse()下面的代码了,如此循环直到结束!不过我觉着最重要的还有一样就是有时候不必刻意去关注的那么细,也要有全局观,例如我们只需要知道函数reverse()是继续处理同样的功能,没必要再去想这个函数里面又是怎么样怎么样的,我感觉肯定会抓狂的!希望跟我一样纠结的朋友不在纠结递归了.........
说明:本人新手,如有不当之处,望指点,谢谢....^.^
另外的一些人的理解:
首先,递归,你可以想想成循环。for循环也可以看作类似递归的东西。但是for循环和递归有个本质区别在于,for循环,是在循环外,进行判断,以决定是否直接退出。而递归,是在循环体内判断,是否退出本次循环。这里就有两个差别了。
1、并不是说for循环不能在循环体内跳出,完全可以,但是希望强调,这个判断是针对循环体独立的,所以我说循环外,而递归的判断是针对每次的具体的循环而不是对整体循环是否停止做判断。
2、for跳出循环体,则前面的循环均不会再重现,而递归是退回到上一次的循环。
上面两者合起来就是我说的比较本质的区别。由此递归,是行为上循环。而每次循环,均属于自己独立的空间。而多次循环是累加,嵌套的。
你可以把递归看做到楼顶取东西。从一楼爬,看,不是的,继续爬,每层楼梯看上去都一样,你执行的过程都一样,但是实际上,1到2,2到3的楼梯是两个楼梯,等你到楼顶了,取了东西,你不能直接就跳楼,还得从楼顶一层层退回来。
而驴子拉磨,则属于for循环。无论跑多少次,都是在原地。变化的只是磨盘里磨的东西,而不是驴每圈所在的不同位置。
函数调用函数的堆栈是系统分配的,这个空间大小和空间在哪,你简单的玩法是改不了,要动堆栈寄存器。现在的电脑,1000不会溢出。呵呵。
堆栈你只能修改两个指针,基指(基地址指针,不是错别字)和变指,基指对于堆栈自身逻辑,唯一的作用就是判断堆栈是否空,而变指是完成先进后出的保证。所以谈堆栈就不存在大小的问题。除非你打算每次堆栈都进行判断修正操作。当然你可以截取MMU的错误中断响应,动态调整堆栈的大小,可以实现,不过没什么必要,工程中如果出现堆栈问题,会尽可能的将该问题移动到判断系统工作负载容量上,确保堆栈现有空间在系统可承受的需求响应下不会溢出,而不是每次堆栈操作判断是否出错,无论你判断还是MMU。