递归思想

递归思想

1.计算连续整数的和

循环解法

// 从 from 一直加到 to
// 循环解法:  
def sum1(from: BigInt, to: BigInt): BigInt = {
    var num = from
    var sum: BigInt = 0
    while (num <= to) {
        sum += num
        num += 1
    }
    sum
}

递归解法

def sum(from : BigInt , to : BigInt):BigInt ={
    if (from == to) from
    else from + sum(from+1 , to)
}
说明:
  • 递归算法,一般来说比较简单,符合人们的思维方式,但是由于需要保持调用堆栈,效率比较低,在调用次数较多时,更经常耗尽内存。
  • 因此,程序员们经常用递归实现最初的版本,然后对它进行优化,改写为循环以提高性能。尾递归于是进入了人们的眼帘。
  • 上面的这个递归调用不是尾递归, 因为有一次额外的加法调用, 这导致每一次递归调用留在堆栈中的数据都必须保留. 所以很容易出现StackOverflowError.

2.尾递归

  1. 尾递归就是指递归调用是函数的最后一个语句, 而且结果被直接返回. 基本上一个栈就够用的,不断地刷新这个栈,重复使用,基本上不会出现栈溢出.
  2. 尾递归一般情况下,参数列表总是比正常递归多一个
def sum1(from :BigInt,to:BigInt,sum :BigInt):BigInt={
    if (from == to ) from + sum
    else sum1 (from +1, to,sum+from)
}
说明:
  • 以上的调用,由于调用结果都是直接返回,所以之前的递归调用留在堆栈中的数据可以丢弃,只需要保留最后一次的数据,这就是尾递归容易优化的原因所在
  • 而它的秘密武器就是上面的sum,它是一个累加器(accumulator,习惯上翻译为累加器,其实不一定非是“加”,任何形式的积聚都可以),用来积累之前调用的结果,这样之前调用的数据就可以被丢弃了。
  • scala 已经对尾递归做了优化, 可以放心使用.(TCO: tail call optimization)
  • 如果自己不确定是否为尾递归, 可以加注解:scala.annotation.tailrec. Scala 可以判断是否为尾递归,如果不是则会报错.
  • 将一个常规递归改写成尾递归并不难。我们可以做预计算,将部分结果放置在参数中,而不是在递归调用方法返回的时候做乘法操作。
斐波那契数列

正常递归

def fibonacci(n: Int): Int = {
    if (n <= 2) 1
    // 不是直接返回函数调用, 而是有 + 运算, 所以不是尾递归
    else fibonacci(n - 1) + fibonacci(n - 2)  
}

尾递归

def fibonacci1(n:Int ,a1 : Int,a2:Int): Int={
    /*
    n表示将来要计算第 n 项. 可以理解成需要计算 n 次
    a2 就是我们要求的值
    假设计算第 5 项:
    第 1 次: f(5, 0, 1)  计算出来第 1 项
    第 2 次: f(4, 1, 1)  计算出来第 2 项
    第 3 次: f(3, 1, 2)  计算出来第 3 项
    第 4 次: f(2, 2, 3)  计算出来第 4 项
    第 5 次: f(1, 3, 5)  ...
 */
    if(n=1) a2
    else fibonacci1(n-1,a2,a1+a2)
}
阶层
def factorical(n:Int,tmp:Long):Long={
    if (n==1) tmp
    else factorical(n-1,tmp*n)
}
字符串反转
def reverse(str:String,tmp:String):String={
    if (str.length ==0) tmp
    else reverse(str.tail,str.head+tmp)
}
计算最大值
def max(arr:Array[Int],m:Int):Int={
    if(arr.length==0) return m
	if(arr.head>m) max(arr.tail,arr.head) 
    else max(arr.tail,m)
}
尾递归的局限
  1. 由于 JVM 的限制,对尾递归深层次的优化比较困难,因此,Scala 对尾递归的优化很有限,它只能优化形式上非常严格的尾递归。

  2. 如果尾递归不是直接调用,而是通过函数值。 不能优化

val foo = fac _
def fac(n:Int,tmp:Long):Long ={
    if (n==1) tmp
    else factorical(n-1,tmp*n)   //不是尾递归
}
  1. 间接递归不会被优化 间接递归(有人叫做蹦床调用 trampoline call),指不是直接调用自身,而是通过其他的函数最终调用自身的递归
def foo(n:Int):Int={
    if(n==0) 0
    else bar(n)
}

//间接调用递归不能被优化

def bar(n:Int):Int={
    foo(n-1)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值