继前面学习的函数,我们今天来学C语言里比较重要的思想--递归与迭代!
那么,什么是递归呢?
递归
递归是一种程序调用自身的编程技巧,自己调用自己,把一个复杂的大问题一层层转化成一个与原问题相似但较小的问题来求解。这样会很好的减少代码量
递归递归,说白了就是传递与回归,递归总有一个传递的过程,也会有回归的过程。
核心思想:大事化小
说了这么多,我们来看看一个关于递归的例子:
例子1
问题:输入1234,返回1 2 3 4
我们来看看解题过程:
我们先将1234%10取出4,然后1234/10将其变成123,然后一直重复直至取出所有的数。
但是会有个问题!
我们取值的时候是倒着取得,也就是说取出来的时候再打印会是4 3 2 1,并不是我们需要的1 2 3 4
正常做法是会创建一个数组,然后存入数组再打印,但是,我们这里来介绍下递归算法:
#include <stdio.h>
//递归:输入1234,返回1 2 3 4
void print(int num)
{
if (num > 9)
print(num/10);
printf("%d ", num%10);
}
int main()
{
int num = 0;
scanf("%d", &num);
print(num);
return 0;
}
这里,我们从main函数开始看,我们创建了m作为那个整形数据1234,然后创建了一个打印函数print,传入参数num。
然后我们开始定义函数内容,我们可以这样看1234,看看有没有什么规律可言
1234,emmm,我们可以看作是先输出123,再输出4
然后,123我们可以看成先输出12,再输出3
12我们又可以先输出1,在输出2
这样,规律就出来了,我这里来讲述下函数定义为什么这么写:
图示流程
先从左边开始看,我们输入1234让num接受这个数据,然后放到打印函数print里,打印函数接收了这个数据就发送给函数的定义那里,此时!num=1234,我们先不管条件,先来看print(num/10)。
这代码意思是将num/10传给函数print,也就是自己调用自己,代码在走到这里时并不会执行下面的语句,而是将num=123传给图二。然后,条件判断成立后我们继续将自己/10再传给自己,num这个时候传给第三级函数时是 = 12,然后继续判断,条件成立,继续将12/10传给自己,这个时候,注意,num=1,不符合条件,所以我们跳过循环成立的内容,执行printf("%d", num%10)这个代码,因为num=1,所以我们打印“1”。
之后并不会直接退出函数,而是执行“归”的操作,图上我们将代码分为四段来解析,最后一段返回到上一段的内容,也就是图三。这个时候,图三的num=12,我们将12%10,打印“2”。
然后继续“归”,图三返回图二,图二num=123,所以123%10 = 3,打印“3”
最后图二返回图一,图一的num=1234,1234%10=4,打印“4”,然后,图一再返回之前调用函数的地方,也就是main函数那里的print,至此,结束!
问题来了,为什么要写 if 去判断呢?
因为递归需要结束的条件!如果没有结束的条件,递归就会变成死递归。而这里我们条件是num < 9,也就是说num在小于两位数后就结束传递了。
我们打印的结果自然就是1 2 3 4了。
这就是递归的思想,将大的问题通过规律来化成小的问题,再一级级解决。
总结条件
我们可以总结一下递归想要成立的两个非常重要的条件:
1. 递归要有停下来的动作(条件)(不能是死递归)
2. 递归要有变小的动作(每次递归后都需要越来越接近条件)
例子2
了解了递归的基本运行,我们来看看著名的斐波那契数列。
斐波那契数列: 1 1 2 3 5 8 13 21 34 55...
我们可以看到规律,斐波那契数列第一与第二个数都是1,从第三个数开始时就是前两数之和。
5 = 3 + 2
8 = 5 + 3
13 = 8 + 5
.....
由此我们借用数学的表达方式可以得出:
我们将函数定义为fib,n 就是需要求的某个数,不难得出 fib = fib(n-1) + fib(n-2) 这个式子
所以我们可以非常轻松的写出代码:
//递归:斐波那契数列
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);
printf("%d\n", fib(n));
return 0;
}
代码很好解释,这里就不详细展开来说了。
但是,使用这个函数写出来会有些问题,就是计算量太大了。我们举个例子,这里设n = 10
我们需要求第10个斐波那契数列的值。
fib(10) 可以拆分成 fib(9) + fib(8)
fib(9) 又可以拆分成 fib(8) + fib(7)
fib(8) 拆分成 fib(7) + fib(6)
你会发现:
10 = 9 + 8
8 + 7 7 + 6
7 + 6 6 + 5 6 + 5 5 + 6
........
越来越多越来越多,非常多都会重复计算,这样代码量虽然笑了但是效率反而低了。
那么,有什么办法可以解决这问题吗?
我们可以使用迭代呀!
迭代
1 | 1 | 2 | 3 | 5 | 8 | 13 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
这里我要求n = 6(也就是8),我们可以令a = 1(第一个), b = 1(第二个)
定义个c,将c = a + b, 这样我们就能求出n = 3了
接下来,我们定义新变量d,然后让a = b , b = c, c = a + b 也就是1 + 2 = 3(第四个)
以此反复,将所需要的数求出来,代码虽然多点但效率提升了非常之多。\
//斐波那契数列
int fib(int n)
{
int a = 1, b = 1, c = 0, d = 0;
if (n == 1 || n == 2)
return 1;
else
{
while (n >= 3)
{
c = a + b;
a = b;
b = c;
d = c;
n--;
}
return ("%d\n", c);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d\n", fib(n));
return 0;
}
这种一级接一级的就是迭代,而n--就是为了控制循环,使其可以退出。
结语
以上就是今日的学习内容啦~因为我一时不知道怎么写所以这篇文章讲的比较的乱/(ㄒoㄒ)/~~
最后,感谢看到这里的所有人!!!我们下次再见啦,拜拜~