Hello大家好! 很高兴与大家见面! 给生活添点快乐,开始今天的编程之路。
1定义:递归即递推和回归,在C语⾔中,递归就是函数⾃⼰调⽤⾃⼰。
2优点:可以把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。
#include <stdio.h> int main () { printf ( "hehe\n" ); main(); //main 函数中⼜调⽤了 main 函数 return 0 ; }

上述就是⼀个简单的递归程序,只不过上⾯的递归只是为了演⽰递归的基本式,不是为了解决问 题,代码最终也会陷⼊死递归,导致栈溢出(Stack overflow)。那玩什么会出现栈溢呢?这是因为代码运行会向内存申请空间,而死递归导致向内存申请空间超过导致栈溢出。
递归在书写的时候,有2个必要条件:
• 递归存在限制条件,当满⾜这个限制条件的时候,递归便不再继续。
•每次递归调⽤之后越来越接近这个限制条件。
但是满足上面二个条件不一定是递归,但不满足上面二个条件一定不是递归。
1 求n的阶乘
计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
1.1分析和代码实现
5 ! = 5 * 4 * 3 * 2 * 1 4 ! = 4 * 3 * 2 * 1
n的阶乘的公式: n! =n*n-1*n-2......*1= n ∗ (n − 1)!
n的阶乘和n-1的阶乘是相似的问题,但是规模要少了n。有⼀种有特殊情况是:当 n==0 的时候,n的阶乘是1,⽽其余n的阶乘都是可以通过上⾯的公式计算。 这样我们就把把⼀个较⼤的问题,转换为⼀个与原问题相似,但规模较⼩的问题来求解
这样就能写出 n 的阶乘的递归公式
(1)n=0: fac(n)=1
(2)n>1: fac(n)=n*fac(n-1)//fac(n)表示n的阶乘
# 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 = Fact(n); printf ( "%d\n" , ret); return 0 ; }
运⾏结果(这⾥不考虑n太⼤的情况,n太⼤存在溢出):
1.2画图推演

或

2 举例2:顺序打印⼀个整数的每⼀位
输⼊⼀个整数m,按照顺序打印整数的每⼀位。
⽐如:
输⼊:1234 输出:1 2 3 4
输⼊:34568 输出:3 4 5 6 8
2.1 分析和代码实现
分析
如果n是⼀位数,n的每⼀位就是n⾃⼰
n是超过1位数的话,就得拆分每⼀位
1234%10就能得到4,然后1234/10得到123,这就相当于去掉了4
然后继续对123%10,就得到了3,再除10去掉3,以此类推
不断的 %10 和 /10 操作,直到1234的每⼀位都得到;
但是这⾥有个问题就是得到的数字顺序是倒着的
那我们假设想写⼀个函数Print来打印n的每⼀位
Print( 1234 ) ==>Print( 123 ) + printf ( 4 ) ==>Print( 12 ) + printf ( 3 ) ==>Print( 1 ) + printf ( 2 ) ==> printf(1)
直到被打印的数字变成⼀位数的时候,就不需要再拆分,递归结束。
2.2 分类
1n<=9 (n为个位数):printf("%d",n)
2 n>10
void Print ( int n) { if (n> 9 ) { Print(n/ 10 ); } printf ( "%d " , n% 10 ); } int main () { int m = 0 ; scanf ( "%d" , &m); Print(m); return 0 ; }
2.3画图推演

或

1递归的好与坏;好处:是⼀种很好的编程技巧,但是和很多技巧⼀样可以把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。
坏处:
在C语⾔中每⼀次函数调⽤,都需要为本次函数调⽤在内存的栈区,申请⼀块内存空间来保存函数调⽤期间的各种局部变量的值,这块空间被称为运⾏时堆栈,或者函数栈帧。
函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。
所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出的问题。
这是 不想使⽤递归,就得想其他的办法, 通常就是迭代的⽅式(通常就是循环的⽅式)。
2 递归与迭代区别
递归的形式更加清晰,不同问题的迭代实现往往⽐递归实现效率更⾼。 当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运 ⾏时开销。
举例:求第n个斐波那契数
计算第n个斐波那契数,是不适合使⽤递归求解呢?斐波那契数的问题通过是使⽤递归的形式描述的。
int Fib ( int n) { if (n<= 2 ) return 1 ; else return Fib(n -1 )+Fib(n -2 ); } # include <stdio.h> int main () { int n = 0 ; scanf ( "%d" , &n); int ret = Fib(n); printf ( "%d\n" , ret); return 0 ; }
但是当我们n输⼊为50的时候,需要花费多久时间才能算出结果呢?这时计算所花费的时间,是我们能难接受的吗?其实递归程序会不断的展开,在展开的过程中,我们很容易就能发现,在递归的过程中会有重复计 算,⽽且递归层次越深,冗余计算就会越多。
# include <stdio.h> int count = 0 ; int Fib ( int n) { if (n == 3 ) count++; // 统计第 3 个斐波那契数被计算的次数 if (n<= 2 ) return 1 ; else return Fib(n -1 )+Fib(n -2 ); } int main () { int n = 0 ; scanf ( "%d" , &n); int ret = Fib(n); printf ( "%d\n" , ret); printf ( "\ncount = %d\n" , count); return 0 ; }

在计算第40个斐波那契数的时候,使⽤递归⽅式,第3个斐波那契数就被重复计算了39088169次,这些计算是⾮常冗余的。所以斐波那契数的计算,使⽤递归是⾮常不明智的,我们就得想迭代的⽅式解决。
int Fib ( int n) { int a = 1 ; int b = 1 ; int c = 1 ; while (n> 2 ) { c = a+b; a = b; b = c; n--; } return c; } 迭代的⽅式去实现这个代码,效率就要⾼出很多了
本篇文章就到此结束,希望有所能帮到 读者更好的了解函数递归与迭代。