当我们在生活中遇到一个复杂问题时,我们会想方设法将其解决,这时我们会有很多种方法,我们可以将问题一步一步顺序化,也可以使用逆向思维将其巧妙化解。C语言中就给我们提供了一种将问题大事化小思想——递归。
什么是递归
程序调用自身的编程技巧称之为递归。
递归作为一种算法在程序设计中被广泛应用。它通过一个大型复杂的问题层层转化成一个个与原问题相似的小问题进行解决,通过自身调用层层嵌套来解决问题,递归只需要少量的程序代码就可以将重复多次的计算进行。
递归的两个必要条件
1.必须要有限制条件,当满足此条件时退出递归,使递归停止。
2.每次递归都要接近限制条件。
这两个条件缺一不可,如果没有将进行无限递归
void hh()
{
printf("haha\n");
return hh();
}
int main(void)
{
hh();
return 0;
}
(如同while死循环),可能会导致电脑死机。
例子
写出7的阶乘
不用递归的方法:
int add(int x)
{
int sum = 1;
for (int i = 1; i <= x; i++)
{
sum *= i;
}
return sum;
}
int main(void)
{
int a = 0;
scanf("%d", &a);
printf("%d\n", add(a));
return 0;
使用递归的方法:
#include<stdio.h>
int add(int x)
{
if (x == 1)
return 1;
else
return x * add(x - 1);
}
int main(void)
{
int a = 0;
scanf("%d", &a);
printf("%d\n", add(a));
return 0;
}
这两端代码都成功的求出7!为5040,但是明显使用递归的代码更加的简洁。那它执行的过程是什么样子的呢?
这个就是函数递归的基本思路,函数中调用自己然后在倒叙返回。
函数递归的缺陷
递归并不是百利而无一害,在这种特定的算法中,有些问题会被递归复杂化,使其计算量成指数增长。最典型的例子就是计算斐波那契数列,当计算的位数过大时,计算机的计算速度肉眼可见的速度减慢。
例:计算第三十个斐波那契数。
//不使用递归
int add(int x)
{
int a = 1;
int b = 1;
int sum = 0;
if (x == 1 || x == 2)
return 1;
for (int i = 3; i <= x; i++)
{
sum = a + b;
a = b;
b = sum;
}
return b;
}
int main(void)
{
int a = 50;
printf("%d\n", add(a));
return 0;
}
在不使用递归的情况下,算第五十个斐波那契数列非常的快,虽然结果是错的!!!但当我们使用递归时效果会是怎样的呢?
//使用递归
int add(int x)
{
if (x == 1 || x == 2)
return 1;
else
return add(x - 2) + add(x - 1);
}
int main(void)
{
int a = 50;
printf("%d\n", add(a));
return 0;
}
计算机一直在计算第五十个斐波那契数,但是时间会非常的长,CPU的占用率也逐步升高。那是因为使用递归会将同一种问题重复计算,已达到想要的结果。
在使用递归算法计算斐波那契数时,计算机就是采用这种树状结构进行相应的计算。计算的数字成指数增加。
int sum = 0;
int add(int x)
{
if (x == 3)
sum++;
if (x == 1 || x == 2)
return 1;
else
return add(x - 2) + add(x - 1);
}
int main(void)
{
int a = 30;
printf("%d\n", add(a));
printf("%d\n", sum);
return 0;
}
在计算第三十个斐波那契数的时候,第三个斐波那契数就计算了317811次,更别说计算总和有多大了。可见递归的缺点就是把计算问题复杂化。虽然可以简化程序,但是会使计算机计算量增大,使其效率大大降低。
所以在遇到问题时,选择合适的方法至关重要,不要让自己轻松从而使时间被白白浪费。