目录
前言
一、递归的概念
二、递归例题详解
例1:斐波那契数列
例2:求次方
例3:求各位数之和
例4:阶乘
例5:顺序打印
三、递归的注意事项
总结
前言
本文将和大家分享一些递归函数的相关知识,技巧与一些经验,希望对大家有所帮助,递归函数作为一种简洁的解题方法和特别的思维方式,非常值得我们去学习,而这种解题思路不太好想,最重要的还是多加练习,文中将举例一些我做过的递归题目来详细介绍递归,希望对大家有所帮助
一、递归的概念
在C语言中,递归简单来说就是函数自己调用自己,但是函数一味的自己调用自己就可能造成死递归,死递归就会导致栈溢出
如:
Stack overflow 即栈溢出
递归应当避免死递归
递归的中心思想:把一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程
递归递归,递即递推的意思,归即回归的意思。
因此,函数递归除了满足函数自己调用自己外,还有两个必要的条件:
- 递归存在限制条件,当满足这个限制条件的时候,递归便不再继续
- 每次递归调用之后越来越接近这个限制条件
以下将通过例题深入体会这两个限制条件
二、递归例题详解
例1:斐波那契数列
题目:使用递归实现求第n个斐波那契数列
题目分析:
- 斐波那契数列:1 1 2 3 5 8 13 21 ...
- 规律:即从第3位开始,该位数等于前两位数之和
递归分析:
- 为了实现递归,我们先找到一个临界条件,也就是递推的终点,回归的起点,该递归函数需要的参数只有n,即第n个斐波那契数列值,我们将函数命名为 Fib(n)
- 斐波那契数列只有从第三位开始才有前两项之和等于该项的规律,也就是当n>2时。当n<=2时,函数只需要返回1,
- 因此,我们可以设置n<=2为这个临界条件,每次调用该函数时都接近这个条件
- 例如,当n=5时,如下图分析:
- 下面我们就实现这个代码并验证这个结果:
这就是使用递归实现求斐波那契数列的代码,我们可以对比观察一下不使用递归实现求斐波那契数列的代码:
(注:该方法为,c为前两项之和,a,b分别为前2项和前1项,每次计算完c,就将b赋值给a,c赋值给b,实现逐步向后计算斐波那契数列)
对比发现:递归函数代码量非常少,却能实现一样的效果
例2:求次方
题目:编写一个函数实现n的k次方,使用递归实现。
分析:
- 众所周知,实现n的k次方,使用库函数pow即可实现,但现在需要使用递归实现,实现n的k次方,即n*n*n*... 递归函数的参数为n和k,我们将该函数命名为my_pow
- 同样,实现递归我们首先需要寻找限制条件,也就是递推与回归的临界条件,条件一般从参数上找,该函数有两个参数,我们先找到最底层的条件,也就是满足该条件时函数一定或应该返回什么。
- 不难想到,当k=1时,函数只需要返回n就行,当k不等于1时,我们需要再次调用该函数并接近这个临界条件,所以我们可以写成n*my_pow(n,k-1)
- 代码实现如下:
- 我们通过画图来理解:
- 当k不等于1时,计算n*my_pow(n, k-1),直到k=1,再逐渐返回计算值,这就是整个递归过程
例3:求各位数之和
题目:写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和
例如:调用DigitSum(1729),则应该返回1 + 7 + 2 + 9,它的和是19
输入:1729,输出:19
分析:
- 该递归函数只有一个参数n,我们需要求每一位上的数之和,就需要得到每一位的数。不难想到,得到一个数的个位数只需要除10取余数就行,也就是%10,而得到十位数只需要先除以10就能消去个位数,再将得到的数%10就能得到十位数
- 我们再来确认限制条件,不难先想到最小的值,也就是n只有一位数时,即n<10时,这时候函数只需要返回n就行。当n为多位数时,我们需要先计算尾数,然后再调用该函数计算剩余的每一位数之和,而调用该函数时的参数需消除尾数,直到n仅剩一位数时递推结束
- 代码实现如下:
- 画图理解:
- 每一次递推将结果拆分为尾数+调用函数,直到n<10依次将结果返回
例4:阶乘
题目:递归实现求n的阶乘(不考虑溢出的问题)
分析:
- 阶乘:如5! = 5*4*3*2*1,阶乘的概念我们应该很清楚,通过5的阶乘我们可以发现 5! = 5* 4!,所以求5!我们可以转换求5*4!,以此类推下去...,除此之外我们需要知道0! = 1
- 因此我们可以将0作为限制条件,当n等于0时,函数返回1即可。当n>0时,函数返回n*(n-1)!即可,直到n等于0,递推结束,我们将函数命名为Factorial
- 代码实现如下:
- 我们画图分析:
- n不等于0时,不断地使n*(n-1),直到n等于0即可逐步返回
例5:顺序打印
题目:递归的方式实现打印整数的每一位数
例如:输入:4321
输出:4 3 2 1
如果前面四道题全部能理清楚,那么这道题就不在话下了,只需要递归满足两个条件,一:限制条件,二:每次调用越来越接近这个条件即可。大家可以自己试着画图分析并且完成这道题
代码实现:
三、递归的注意事项
- 关于递归和迭代的选取
当我们学会递归后是不是发现递归的代码非常简单,并且递归和迭代相似,像一种套娃式的循环,那我们是不是遇到类似的问题就都使用递归呢?
答案是否定的,递归虽好,却也有着局限
递归的局限性:如果计算数值过大,递归中就会存在大量重复计算,导致计算效率降低
例如我们的第一道例题:求斐波那契数列
假如我们使用递归求第50个斐波那锲数列,我们会发现计算机半天计算不出来
而我们使用迭代的方式发现,虽然计算结果是错的,但计算效率非常高,(结果错误是因为int类型存储大小的限制)
为什么递归半天计算不出结果,我们通过画图来解释:
我们可以发现,单就一个Fib(46)就计算了4次,而且越往下,重复计算就越多
我们可以打印看看求Fib(40),Fib(3)被计算了多少次:
Fib(3)居然被计算了三千多万次,这样大量的重复计算会导致代码效率非常低下
因此对于求斐波那契数列,用迭代的方式效率会高很多
总之,递归虽好,但也不是万能的,要根据实际问题选择是使用递归还是迭代
总结
以上就是关于递归函数的一些知识,希望对大家有所帮助,感谢支持。