问:何为递归函数?
说人话:本身调用本身的函数就叫递归函数。java
实现一个递归函数,我将其归纳为是一个“推卸责任”的过程,分为3个步骤:算法
咱们将用求阶乘的例子来详细解释这3个步骤。数组
首先,明确输入输出:咱们但愿只要给该方法/函数一个整数,就能返回它的阶乘。那么该递归方法的方法签名即为以下:数据结构
public int factorial(int n){};
这样咱们就完成了实现递归函数的第1个步骤了。函数
很显然阶乘的边界状况就是求1的阶乘,它没有理由再把计算的责任推卸给任何人了,由于它本身就能直接得出计算结果1,因此求1的阶乘就是咱们要找的那只“替罪羊”。spa
所以,基准状况即为以下:code
if (n == 1) return n;
提早找好了“替罪羊”,接下来咱们只须要把“责任推卸链”完成,让责任一级一级最终推卸到替罪羊身上,就能够大功告成!递归
而要完成这条“责任推卸链”,其实就是列出关于原问题和子问题的数学等价关系式。ci
对于求阶乘而言,原问题和子问题的等价关系式为:\(n! = n \times (n - 1)!\)。这样,就将问题从第\(n\)层成功地转移到了第\(n-1\)层。get
那么对应的代码实现即为以下:
else return n * factorial(n - 1);
综合以上3个步骤,完整版的实现阶乘的递归函数即为:
/** Returns n factorial. */ public int factorial(int n) { // Base case if (n == 1) return n; // Recursive case else return n * factorial(n - 1); }
在这个递归函数里,咱们定义了递归状况来一步步简化问题,也定义边界状况来计算出最终的边界值。因此咱们大可放心地调用该函数,让它自行去解决问题。
看到这里,相信你已经掌握了递归函数的写法。能够看出,写出一个递归函数并无那么容易。而咱们可不但愿当咱们绞尽脑汁憋出一个递归函数后,却换来老板一句“画蛇添足,净瞎折腾”。
那要避免这种状况,咱们得先弄清楚一个前提:何时派上递归最为合适?
咱们先来谈谈递归的缺点:
n = 5
时,须要计算一遍的fibonacci(3)
,推导到n = 4
时,又须要计算一遍fibonacci(3)
。也就是说求一个5的阶乘须要计算两遍的fibonacci(3)
,因此说时间效率低。而递归主要是用于如下两种状况:
因此,除非是以上两种状况,不然尽可能避免使用递归,以免对空间和时间产生大的消耗。(P.S. 把浪费的这些功夫拿去打王者荣耀它不香吗...)
递归函数是本身调用本身的函数。经过列出方法签名,完成基准状况和递归状况便可完成一个递归函数。可是用递归实现一般会对时间和空间产生大的消耗,所以除非数据结构自己就是按递归的形式定义或是问题能以一样的解法逐级减少问题规模,不然应尽可能避免使用递归。
创做不易,点个赞再走叭~