赞
踩
本篇介绍的是C语言函数递归的详细知识
程序的艺术来源于生活
目录
程序调用自身的编程技巧称为递归
(函数自己调用自己)
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解递归策略
只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思想在于:把大事化小
例(史上最简单的递归):
- #include<stdio.h>
- int main()
- {
- printf("Hello World\n");
- main();
- }
先一直打印 Hello World,最终程序挂掉
- 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
- 每次递归调用之后越来越接近这个限制条件
接受一个整型值(无符号),按照顺序打印它的每一位。
例如:
输入:1234,输出 1 2 3 4
我们的第一想法是先让
- 1234%10 = 4
- 1234/10=123
- 123%10=3
- 123/10=12
- 12%10=2
- 12/10=1
- 1%10=1
- 1/10=0
这样能够得到数字的每一位,然后我们再打印出来
- #include<stdio.h>
- int main()
- {
- unsigned int num = 0;
- scanf("%u", &num);
- while (num)
- {
- printf("%d ", num % 10);
- num = num / 10;
- }
- return 0;
- }
程序一进行我们发现输出的结果与题目中的不同,需要我们用递归来进行
递归的思想是将大事化小
我们定义一个print()函数,它的功能是按照顺序打印num的每一位
打印1234的每一位:print(1234)
打印1234的每一位,我们可以先打印123的每一位再打印4
打印123的每一位:我们可以先打印12的每一位print(12)再打印3
打印12的每一位:我们可以先打印1的每一位print(1)再打印2
最后输出打印1 2 3 4
程序:
print(1234)
print(123) 4
print(12) 3 4
print(1) 2 3 4
最终输出:1 2 3 4
当num<10的时候我们只需要直接打印就行
当num>=10.我们就需要分着打印
代码实现:
- #include<stdio.h>
- void print(unsigned int n)
- {
- if (n < 10)//限制条件
- printf("%d ", n);
- else
- {
- print(n / 10);//每次递归调用之后越来越接近这个限制条件
- printf("%d ", n%10);
- }
- }
- int main()
- {
- unsigned int num = 0;
- scanf("%u", &num);
- print(num);
- return 0;
- }
当然也有更简单不冗余的写法:
- #include<stdio.h>
- void print(unsigned int n)
- {
- if (n > 9)限制条件
- {
- print(n / 10);每次递归调用之后越来越接近这个限制条件
- }
- printf("%d ", n % 10);
- }
- int main()
- {
- unsigned int num = 0;
- scanf("%u", &num);
- print(num);
- return 0;
- }
编写函数(不允许创建临时变量),求字符串的长度
铺垫(编写函数(允许创建临时变量),求字符串的长度):
我们有一个专门求字符串长度的库函数:strlen
首先我们先创建一个字符数组
char arr[] = "abc";
再用strlen
- #include<stdio.h>
- #include<string.h>
- int main()
- {
- char arr[] = "abc";
- int len = strlen(arr);//传递的首元素地址
- printf("%d", len);
- return 0;
- }
这题是让我们创建一个函数my_strlen,模仿写一个strlen函数
非递归方式:
- #include<stdio.h>
- int my_strlen(char* str)
- {
- int count = 0;
- while (*str != '\0')
- {
- count++;
- str++;
- }
- return count;
- }
- int main()
- {
- char arr[] = "abc";
- int len = my_strlen(arr);
- printf("%d", len);
- return 0;
- }
编写函数(不允许创建临时变量),求字符串的长度(递归方式):
如果第一个字符不是'\0',说明字符串里面至少包含一个字符
所以当第一个元素不是'\0'的话
求my_strlen("abc")就可以看成求1+my_strlen("bc")的长度
求1+my_strlen("bc")的长度就可以看成 求1+1+my_strlen("c")的长度
求1+1+my_strlen("c")的长度就可以看成 求1+1+1+my_strlen(" ")的长度
最后算出来等于3
代码实现:
- #include<stdio.h>
- int my_strlen(char* str)
- {
- if (*str != '\0')
- return 1 + my_strlen(str + 1);//传递下一个字符的地址
- else
- return 0;
- }
- int main()
- {
- char arr[] = "abc";
- int len = my_strlen(arr);
- printf("%d", len);
- return 0;
- }
求n的阶乘。(不考虑溢出)
递归:
- #include<stdio.h>
- int Fac(int n)
- {
- if (n <= 1)
- return 1;
- else
- return n * Fac(n - 1);
- }
- int main()
- {
- int n = 0;
- scanf("%d",&n);
- printf("%d",Fac(n));
- return 0;
- }
但当我们输入大的数字时,递归会算不出结果
所以这道题最好用非递归的方式(迭代)计算:
(循环是一种迭代)
- #include<stdio.h>
- int Fac1(int n)
- {
- int i = 0;
- int ret = 1;
- for (i = 1; i <= n; i++)
- {
- ret = ret*i;
- }
- return ret;
- }
- int main()
- {
- int n = 0;
- scanf("%d",&n);
- printf("%d",Fac1(n));
- return 0;
- }
求第n个斐波那契数。(不考虑溢出)
什么是斐波那契数列
分为两部分
按照公式写代码
- #include<stdio.h>
- 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",Fib(n));
- return 0;
- }
但是我们发现这样会有问题:
- 在使用 Fib 这个函数的时候如果我们要计算第50个斐波那契数字的时候特别耗费时间。
- 使用Fac函数求10000的阶乘(不考虑结果的正确性),程序会崩溃。
原因是因为
Fib 函数在调用的过程中很多计算其实在一直重复
那我们如何改进呢?
- 在调试 Fac函数的时候,如果你的参数比较大,那就会报错: stack overflow(栈溢出)这样的信息。
- 系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出
如何解决上述问题
1. 将递归改写成非递归。
2. 使用static对象替代 nonstatic 局部对象。在递归函数设计中,可以使用 static 对象替代
nonstatic 局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且 static 对象还可以保存递归调用的中间状态,并且可为各个调用层所访问
用非递归方式解决斐波那契数列
- #include<stdio.h>
- 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;
- }
-
- int main()
- {
- int n = 0;
- scanf("%d", &n);
- int ret = Fib(n);
- printf("%d\n", ret);
- return 0;
- }
提示:
1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销
结语:本篇是函数递归的知识,如果本篇对你有用,请大家点赞关注!!!
现在关注是新粉,以后就是老粉了
下期预告:数组
感谢你的观看,我们下期再见
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。