赞
踩
递归过程中,栈维护了每个函数调用的信息直到函数返回后才释放,这需要占用相当大的空间,尤其是程序中使用了许多递归调用的情况下,除此之外,因为有大量的信息需要保存和回复,因此生成和销毁活跃记录需要耗费一定的时间。如此一来函数调用的开销变得很大时,我们就需要考虑应该采用迭代的方案。幸运的是,我们可以采用一种称为尾递归的特殊递归方式来避免之前提到的缺点。
以求4的阶乘为例,普通递归的写法:
- int fact(int n) {
- if(n < 0) {
- return 0;
- }
- else if (n == 0) {
- return 1;
- }
- else if(n == 1) {
- return 1;
- }
- else
- return n * fact(n - 1);
- }
尾递归写法:
- int facttail(int n,int tmp){
- if(n < 0){
- return 0;
- }
- else if(n == 0) {
- return tmp;
- }
- else {
- return facttail(n - 1,n * tmp);
- }
- }
尾递归定义:一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点就是在回归过程中不做任何操作,大多数现代编译器会利用这种特点自动生成优化代码。
当编译器检测到一个函数调用是尾递归时,它就覆盖当前的活跃记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一行待执行的语句,于是这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。通过覆盖当前的栈帧而不是在其之上重新添加一个,这样做可以大幅节省空间,使得实际运行效率变得更高。
例题:尾递归的方式求某个数的素因子集合。
这段代码很多地方都还可以优化,我写着玩的。
- #include<stdio.h>
- #include<math.h>
- #include<string.h>
- const int N = 10000; //最大范围
- bool isprime[N]; //素数筛选过程中留下来的数组,筛选完毕之后可以用来判断某数是不是素数
- int prime[N]; //保存筛选出来的素数的数组
- int prime_elem[N]; //素因子的数组
- int count; //素因子总数
- int prime_count; //范围内素数总数
- int n;
- void Init(){
- prime_count = 0;
- count = 0;
- memset(isprime,true,sizeof(isprime));
- isprime[0] = false; isprime[1] = false;
- for(int i = 2,j = 0; i * 2< N; i++){
- if(isprime[i]) {
- prime_count++;
- prime[j++] = i;
- for(int j = 2; j * i < N; j++) {
- isprime[i * j] = false;
- }
- }
- }
- }
- int solve(int tmp){
- if(isprime[tmp] && prime_elem[count - 1] != tmp){
- prime_elem[count++] = tmp;
- return 0;
- }
- else {
- for(int i = 0; i < prime_count; i++){
- if(tmp % prime[i] == 0){
- if(prime_elem[count - 1] != prime[i])
- prime_elem[count++] = prime[i];
- tmp /= prime[i];
- break;
- }
- }
- }
- return solve(tmp);
- }
- void output(){
- for(int i = 0; i < count; i++){
- printf("%d%c",prime_elem[i],(i == count - 1 ? '\n' : ' '));
- }
- }
- int main(){
- // freopen("in.txt","r",stdin);
- Init();
- while(~scanf("%d",&n)){
- count = 0;
- memset(prime_elem,0,sizeof(prime_elem));
- solve(n);
- output();
- }
- return 0;
- }
总结,尾递归就是把递归放在递归函数的最尾,编译器识别后会自动优化
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。