【C语言】——尾递归

一、 什么是尾递归

1.1、 尾递归是什么

  尾递归是指一个递归函数在调用自身时,该递归调用是函数的最后一条语句。换句话说,函数在调用自身之后不再执行任何操作,而是直接返回递归调用的结果。这种特殊形式的递归称为尾递归

  尾递归对于编译器优化非常重要,因为编译器可以将尾递归优化为循环结构,从而避免递归调用过深导致栈溢出的问题。在使用递归解决问题时,如果能够确保递归调用是尾递归,那么可以提高程序的效率和性能。

1.2、 举例理解尾递归

  我们来看个例子:

void fn(int n)
{
	if (n > 0)	
		printf("%d", n);
	return fn(n - 1);
}

  上数函数就是一个尾递归,因为return fn(n - 1);它的递归调用,是最后一个语句

  如果将函数最后一句改为:

return n * fn(n - 1);

  那它还是不是尾递归呢? 不是!
  
  因为函数在执行调用自身的指令后,还要执行 ∗ n *n n 的操作,并没有直接返回递归调用的结果,所以并不是尾递归。

  同时,我们发现,该语句只有当 fn(n - 1) 的值回归后,才能执行 ∗ n *n n 指令,即回归时调用
  而尾递归所有语句都是在递推的时候调用,而不是回归时调用。

二、 递归与尾递归的转化

  下面,我们直接上题目:计算 n n n 的阶乘

2.1、 递归方法

#include<stdio.h>
//递归函数
int Fact(int n)
{
	if (n == 0)
		return 1;
	else
		return n * Fact(n - 1);
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = 0;
	ret = Fact(n);
	printf("%d\n", ret);
	return 0;
}

  该方法的深入讲解可以阅读《【C语音】——函数递归》 ,这里就不再赘述

2.2、 尾递归方法

解题思路:
  
  尾递归是递归的一种,我们初学可以在上面递归方法的代码基础上修改为尾递归。

(1)
  
  首先,既然尾递归要求“递归调用是函数的最后一条语句”,那上述函数最后一句:

return n * Fact(n - 1);

  须改为:

return Fact(n - 1); 

(2)
  
  这时,函数显然无法完成阶乘运算,因为函数本身没有进行任何相乘的运算,同时也没有变量来存放进行阶乘运算后的结果。
  我们可以创建一个 int sum 变量来存放运算结果。而 sum 显然是要进行递归的,因此我们把 sum放在形参位置,相乘运算也是同理。
  

修改如下:

int Fact(int n, int sum)
 { 	
     if (n == 0) 		
         return 1; 	
     else
         return Fact(n - 1, n * sum); 
} 

  

(3)

  最后我们须将返回值 1 改为 sum ,不然不管输入什么值结果都是 1 ,那就尴尬了。
  这时,我们的函数调用 ret = Fact(n); 也要改为 ret = Fact(n, 1);

  
完整参考代码如下:

#include<stdio.h>

int Fact(int n, int sum)
{
	if (n == 0)
		return sum;
	else
		return Fact(n - 1, n * sum);
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = 0;
	ret = Fact(n, 1);
	printf("%d\n", ret);
	return 0;
}

  
图示:

在这里插入图片描述

  
  

三、 练习:用尾递归完成斐波那契数列

  
斐波那契数列公式:

在这里插入图片描述

  看到公式的第一眼,相比大家都会使用递归的方法来解,但用递归来解效率很低(详情请看: 《【C语音】——函数递归》 )。
  那尾递归实现起来又是怎样的呢,让我们一起来看看
  
参考代码:

#include<stdio.h>

int Fib(int n, int last_last, int last)
{
	if (n <= 2)
		return last;
	else
		return Fib(n - 1, last, last + last_last);
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = 0;
	ret = Fib(n, 1, 1);
	printf("%d\n", ret);
	return 0;
}

图示:

这里是引用

  
  对比递归与尾递归,我们发现:尾递归减少许多重复的计算,同时,递归是创建的函数栈帧的层次像二叉树一样呈指数级增长(详情请看: 《【C语音】——函数递归》 ),而尾递归的层次则像数列,呈线性增长。简单来说,原本栈是先扩展开,然后边收拢边计算结果,现在却变成在调用自身的同时通过参数来计算。因此,合理的将递归改为尾递归,能大大提高代码效率。
  
  

  好啦,本期关于尾递归就介绍到这里啦,希望本期博客能对你有所帮助,同时,如果有错误的地方请多多指正,让我们在C语言的学习路上一起进步!

  • 25
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值