一、什么是递归?
!!!递归就是函数自己调用自己!!!
# include <stdio.h> int main () { printf ( "hehe\n" ); main(); //main 函数中⼜调⽤了 main 函数 return 0 ; } 这里只是做一个简单的示例,这种写法是错误,会使程序陷入死递归导致 Stack overflow(栈溢出)。
递归的思想:
把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。
递归中的递就是递推的意思,归就是回归的意思。
二、递归的限制条件
① 递归存在限制条件,当满⾜这个限制条件的时候,递归便不再继续。
② 每次递归调⽤之后越来越接近这个限制条件。
三、递归的举例
递归求n的阶乘
int Fun(int n) { if (n <= 1) return 1; else return n*Fun(n - 1); } int main() { int n; printf("请输入所要求的阶乘:"); scanf_s("%d", &n); int count = Fun(n); printf("该数字的阶乘为:%d", count); return 0; }v
//画图演示 递推过程: Fun(5) ------> Fun(4) ------> Fun(3) ------> Fun(2) ------> Fun(1) =5*Fun(4) =4*Fun(3) =3*Fun(2) =2*Fun(1) =1 回归过程: ||| Fun(5) <------ Fun(4) <------ Fun(3) <------ Fun(2)<------- Fun(2)=120 =2
顺序打印整数的每一位
void Print(int n) { if (n > 9) { Print(n / 10); } printf("%d ", n % 10); } int main() { int m = 0; printf("请输入要打印的数字:"); scanf_s("%d", &m); printf("最终结果为:"); Print(m); return 0; }
四、递归与迭代
对于上述的Fun函数是可以产⽣正确的结果,但是在递归函数调⽤的过程中涉及⼀些运⾏时的开销。
在C语⾔中每⼀次函数调⽤,都要需要为本次函数调⽤在栈区申请⼀块内存空间来保存函数调⽤期的各种局部变量的值,这块空间被称为 运⾏时堆栈 ,或者 函数栈帧 。
函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归 函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。 所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能 引起栈溢 出(stack over flow)的问题 。
顺序打印整数每一位在内存空间中的演示:
!!!递归层次不能太深,太深就会出现栈溢出的问题!!!
所以如果不想使⽤递归就得想其他的办法,通常就是迭代的⽅式(通常就是循环的⽅式)
迭代实现n的阶乘:
int Fact(int n) { int i = 0; int j = 1; for (i = 1; i <= n; i++) { j *= i; } return j; } int main() { int n; printf("请输入所要求的阶乘:"); scanf_s("%d", &n); int count = Fact(n); printf("该数字的阶乘为:%d", count); return 0; }
事实上,我们看到的许多问题是以递归的形式进⾏解释的,这只是因为它⽐⾮递归的形式更加清晰, 但是这些问题的迭代实现往往⽐递归实现效率更⾼。 当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运⾏时开销。
求斐波那契数列:
斐波那契数列:1 1 2 3 5 8 13 21 24......
#include <stdio.h> int count = 0; int Fid(int n) { if (n <= 0) return 0; else if (n == 1) return 1; else count++; return Fid(n - 1) + Fid(n - 2); } int main() { int n = 0; printf("请输入要求第几个斐波那契数列中的数字:>"); scanf_s("%d", &n); int ret = Fid(n); printf("该数字为:%d\n", ret); printf("需要计算的次数为: %d\n", count); return 0; }
但是如果是要求第40个斐波那契数呢?
很明显在我们求第四十个斐波那契数的时候第三个斐波那契数将会被计算165580140次,这个数字太大了计算这个数所需要的内存空间也会很大不划算。
所以我们可以直接通过迭代来实现求解斐波那契数列:
#include <stdio.h> int count = 0; int Fun(int n) { int a = 1; int b = 1; int c = 1; while (n > 2) //当n>2时开始进行循环相加 { c = a + b; a = b; b = c; count++; n--; } return c; //当n<2时直接输出1 } int main() { int n = 0; printf("请输入要求第几个斐波那契数列中的数字:>"); scanf_s("%d", &n); int num = Fun(n); printf("该数字为:%d\n", num); printf("所需要的次数为:%d",count); return 0; }
很明显用迭代来解决该问题运算次数明显减少。
青蛙跳台阶问题:
一、问题描述
有一只🐸,一次可以跳一个台阶,也可以跳2个台阶,如果有n个台阶,这只🐸有多少种跳法,跳上n个台阶?
二、问题分析
第一次跳有两种情况:
1)当第一次跳一个台阶时: 剩余台阶数为n-1
2)当第一次跳两个台阶时: 剩余台阶数为n-2
所以当n>2时,跳法总数=第一次跳一个台阶之后的跳法数+第一次跳两个台阶之后的跳法数
而对于n=2或者n=1时,显然易见此时的跳法总数固定,分别为2或1。
tips:对于后者我们只需要进行简单的if判断即可。
int Jump(int n) { if (n == 2) return 2; else if (n == 1) return 1; else if (n > 2) { int count = Jump(n - 1) + Jump(n - 2); return count; } } int main() { int n; printf("青蛙要跳跃的台阶总数为:>"); scanf_s("%d", &n); int count = Jump(n); printf("青蛙跳完所有台阶的方法总数为:%d", count); }
汉诺塔问题:
一、问题描述
将A柱上的盘子,借助于B柱,全部挪到C柱子上,挪动过程中A,B,C柱上的盘子要满足上面小下面大
二、问题分析
1)如果A只有一层:A(1)---->C(1) 1次移动
大概就是这么个意思,下面的两种就不再演示了
2)如果A有两层:A----->B A----->C B----->C 3次移动
3)如果A有三层:A----->C A----->B C----->B A------>C B----->A B----->C A----->C 7次移动
4)如果A有n层:2^n-1 次移动
具体代码实现如下:
#include <stdio.h> void move(char pos1, char pos2) { printf("%C ->%c ", pos1, pos2); } //n代表盘子的个数 //pos1:起始位置 //pos2:中转位置 //pos3:目的位置 void Hanoi(int n, char pos1, char pos2, char pos3) //主运行的 { if (n == 1) //如果初始位置只有一个方块就直接把它移到目的位置即可 { move(pos1, pos3); } else { Hanoi(n - 1, pos1, pos3, pos2); move(pos1, pos3); Hanoi(n - 1, pos2, pos1, pos3); } } int main() { Hanoi(1, 'A', 'B', 'C'); printf("\n"); Hanoi(2, 'A', 'B', 'C'); printf("\n"); Hanoi(3, 'A', 'B', 'C'); printf("\n"); }