文章目录
- 函数递归
- 函数的迭代
什么是函数递归?函数自己调用自己的编程技巧我们称为递归,函数递归通常会把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需要少量的程序就可以描述出解题过程需要的多次重复计算,大大减少了程序的代码量。递归的主要思考方式在于把复杂问题简单化,把大事化小。
我们在一段代码中演示一下函数递归
#include
void print(int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
int num = 1234;
print(num);
return 0;
}
上述代码我们使用递归的方法,实现了按顺序打印一个整型值的每一位,那么具体的是如何实现的,我们通过画图的方式,来让大家有更直观的体会。
我们说函数递归分为两个部分,先需要递推之后我们还要在回归回来,向上面的代码一样我们将num
的值传给参数n
,之后进行判断如果n>9
我们需要再次调用函数并把参数n
除10,直到n<9
时我们打印n%10
,这个过程我们称为递推的过程,之后们在回归,执行一次后返回上层函数执行完本层函数,在返回上一层函数,最后回到我们的主函数,执行结束。
通过这个代码的过程我们发现,函数递推必须有两个条件
1.函数递推需要存在限制条件,当满足这个限制条件时,递推便不在继续
2.每次递归调用之后越来越接近这个个限制条件
总结一下,这个限制条件含义就在于,就是函数递归调用不能无限次进行,必须有一个约束条件。如果没有这个约束条件会出现什么状况呢?
如图当我们删除了函数递归的限制条件,编译器会提示我们可能存在堆栈溢出的现象也就是我们说的死递归了,因为我么每次调用函数都会在栈区开辟一块内存空间,当我们无限次递归会一直开辟新的内存空间,而我们栈区的空间是有限的,所以当递归到一定限度时就会出现Stack overflow——栈溢出。
我们说其实循环就是一种迭代的方式,那为什么我们已经有递归这样可以简化代码的编程技巧,还要出现迭代呢?有什么用呢?我们在代码中找寻答案。
求第n个斐波那契数,斐波那契数就是1 1 2 3 5 8 13 21 34 55…这样排列组合的数,我们可以看到从第三个数开始,他的值为前两个数之和。
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
int main()
{
int n = 0;
scanf("%d", &n);
int num = fib(n);
printf("%d ", num);
}
通过我们的分析我们可以很容易使用函数递归写出代码,看起来也非常简单,运行也没有问题,但是但是当我们想求第50个斐波那契数时我们发现
并没有输出任何数,那是我们的代码写错了嘛,其实不是,没有输出的原因在于,我们进行了太多的重复计算,太耗费时间了,不信我们可以计算一下第三个斐波那契数被计算了几次
当我们计算第40个斐波那契数时,第三个斐波那契数已经被计算了39088169次,那我们改如何解决这样的问题,我们对代码进行优化。
int fib(int n)
{
int c = 1;
int a = 1;
int b = 1;
while (n > 2)
{
n -= 1;
c = a + b;
a = b;
b = c;
}
return c;
}
int main()
{
int n = 0;
scanf("%d", &n);
int num = fib(n);
printf("%d \n", num);
}
我们才用迭代的方式替换递归,同样可以实现我们的需求
我们再次计算第50个斐波那契数
我们可以看到很快就输出了结果,输出结果不正确是因为数字太大了而已。
总结一下:
1.通过上述案例我们可以发现,递归可以很好的对问题进行解释,尤其是有公式类型的,他比非递归更加清晰
2.但是这些问题的迭代实现往往比递归实现效率更高
3.当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开
销
所以具体使用什么编程技巧还是需要看我们的实际需求,好了说到这递归和迭代的基本知识点,我都与大家分享了,之后为了更好的理解,我会大家分享两道关于函数递归的典型例题。