前段时间,考研群有同学问起递归到底怎了理解,递归怎么使用。今天就写下此篇推文,带同学们好好理解一下什么是递归?递归到底怎么使用!
定 义
程序调用自身的编程技巧称为递归( RecuRsion)。递归作为一种算法在程序设计语言中广泛应用。一个方法或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归是一种非常重要的算法思想,无论是在大厂的算法面试题中还是开发工作中,都是经常用到的。
例如求和问题:若要对1+2+3+……+100进行求和,我们可以通过循环的方法进行求解,代码如下:
那么,如何通过递归方式求解呢?我们发现 1 + 2 + 3 + 4 + …. + 100 可以分解为 ( 1 + 2 + 3 + 4 + …. + 99) + 100,可以看出S100 = S99 + 100,可以得出 Sn = Sn-1 + n。通过递归的方式代码如下:
递归的特点
事实上,递归有两个明显的特点,终止条件(递归出口)和自身调用。那么什么样的问题可以才可以使用递归的方式求解呢?
①自身调用:原问题可以分解为若干子问题,子问题和原问题有同样的求解方法,且子问题比原问题更容易求解。
回到上述的求和的例子,再来看下递归的特点:
哪些问题我们可以可以使用递归来解决呢?
☛阶乘问题
☛斐波那契数列
☛二叉树遍历问题
递归的解题思路
☛第二部:寻找递归终止条件
☛第三步:递推函数的等价关系
没错,这就是递归的三板斧,看文字有点抽象,我们就拿最简单的阶乘来举例子康康吧~
▍定义函数功能
首先,这个大家都是知道的,我们定义定义一个函数是需要使用它的,所以我们得清楚函数的干嘛用的。比如,你要解决阶乘问题,定义的函数功能就是n的阶乘,如下:
递归的一个终止条件就是必须要有一个终止的条件,也就是不能无限循环地递归调用自己。所以,用递归的思路去解决问题的时候,就需要寻找递归终止条件是什么。正如,在n == 1时,不能再递归下去,可以跳出循环,因此 n == 1 可以作为递归的终止条件,如下:
▍递归函数的等价关系
递归的本质就是原问题可以分解为若干子问题,且若干子问题更容易解决。即『原问题和子问题都可以用同一个函数关系表示。地推函数的等价关系式,这个步骤就等价于寻找原问题与子问题的关系,如何用一个公式把这个函数表示清楚』。阶乘的公式就可以表示为:f(n) = n*f(n-1)。因此,递归程序可以写成如下:
实 例
▍阶乘问题
阶乘问题的数学表达式为:n! = n * (n-1) * (n-2) * …* 1 (n>0)。通过分析可以得出n! = (n-1)! * n。令F(n) = n!,则F(n) = F(n-1) * n。则阶乘问题符合条件(1)。由0! = 1,可以得出F(0) = 1。则阶乘问题符合条件(2),递归出口为F(0) = 1。利用递归求解阶乘问题代码如下:
▍斐波那契数列
斐波那契数列(Fibonacci sequence),、因数学家列昂纳多·斐波那契(LeonaRdoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:
1、1、2、3、5、8、13、21、34、……
F(1)=1,F(2)=1,,F(n) = F(n-1) + F(n-2)(n>=3,n∈n*)。
问题分析:
斐波那契数列的对于原问题F(n)的求解可以转为对F(n-1)、F(n-2)两个子问题的求解,故符合条件(1)。由F(1)=1,F(2)=1,可以得出斐波那契数列问题是有递归出口的,递归出口对应F(1) = 1,F(2) = 1。求解斐波那契数列的代码如下:
▍二叉树的遍历
通过代码可以看出,二叉树的遍历过程使用递归方式实现既有助于理解,又简化了代码量。
▍翻转二叉树
输入:
输出:
我们按照上面写的递归函数的三板斧来解题:
①『定义函数功能』
首先我们要明确的是,函数的功能是将给定的二叉树进行反转,所以函数可以定义为:
这棵二叉树在什么时候不用反转了呢?当然是当前结点为nULL的时候或则当前结点为叶子结点的时候。因此,递归函数的终止条件(即递归出口)是:
③『递推函数的等价关系式』
原问题是你要翻转一棵二叉树,那么是不是可以拆分为子问题,我们分别去翻转该二叉树的左右子树?子问题是翻转它的左右子树,是不是又开一拆分为,翻转该左子树的左子树,以及它左子树的右子树呢?然后一直翻转到叶子结点为止。那么,就让我们来康康图解喽~
首先,要翻转根结点为4的二叉树,就需要「 翻转它的左子树(根结点为2)和它的右子树(根结点为7)」。这个就是递归的递的过程了。
然后,根结点为2的树,不是叶子结点,需要继续「翻转它的左子树(根结点为1)和右子树(根结点为3)」。因为1和3都是叶子结点,所以就可以返回了,这也是递归的递的过程~
同理,根结点为7的子二叉树,也不是叶子结点,需要翻转「它的左子树(根结点为6)和右子树(根结点为9)」。因为6和9都是叶子结点,所以也被返回了。
左子树(根结点为2)和右子树(根结点为7)都被翻转完后,这几个步骤就是归的过程,翻转的任务就完成了~
从上面的推导过程,我们可以总结出「递推关系式」就是:
invertTree(bt) = invertTree(bt->lchild) + invertTree(bt->RcHild)
因此,我们可以很容易的写下如下代码:
我们上述的翻转二叉树的代码就完成了,简直无情~
往期推文集合
总群
625590924
调剂群
951508829
广大
1143982604
暨大
1071137230
广工
1093732052
华工
428389734
深大
729770764
浙大
978938582
厦大
1125268501
中大
921801084
南航
281118241
华农
515681663
重邮
736197896
北邮
1126650806
南邮
1109929146
广外
976231252
东北大学
1128523098
华南师大
476784448
南昌大学
923249141
给个“在看”支持一下我