递归
阶乘
一个正整数的阶乘(factorial)是所有小于及等于该数的的积,并且0的阶乘为1。自然数n的阶乘写作n!。1808年,基斯顿·卡曼引进这个表示法。
Code
- 创建一个fact函数
- 参数n为待求阶乘的数
- 返回值为阶乘结果
def fact(n : Int) :Int={
if (n==1) 1 else n * fact(n-1)
}
优点
- 代码符合人类思维
- 代码简洁
缺点
-
重复计算:递归中又很多计算都是重复的,递归的本质时把一个问题分解成两个或多个小 问题,多个小问题存在重叠的部分,即存在重复计算,如斐波那契数列的递归实现。
-
时间和空间的消耗比较大:递归由于是函数调用自身,而函数的调用时消耗时间和空间的,每一次函数调用,都需要在内存栈中分配空间以保存参数,返回值和临时变量,而往栈中压入和弹出数据也都需要时间,所以降低了效率。
-
容易产生StackOverflow异常:在JVM中每个线程包含一个私有的虚拟机栈,栈内存有栈帧(一个方法对应一个栈帧)由于递归函数反复调用自身,虚拟机栈中栈帧会不断累计最终出现StackOverflow
改进尾递归
如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。
@tailrec
//判断函数是否为尾递归
def tailFact(n: Int): Int = {
@tailrec
def loop(n: Int, currResult: Int): Int = {
if (n == 1) currResult else loop(n - 1, currResult * n)
}
loop(n, 1)
}
每一次调用loop在虚拟机栈中会移除上一个loop的栈帧然后在讲当前loop的栈帧压入栈内