Golang 尾递归和递归比起来优势在哪?
Golang轻松学习一、递归与尾递归是什么?
1.递归
递归就是一个函数直接或间接地调用自身,是为直接或间接递归。递归主要是通过把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归就是用少量的程序来描述出解题过程所需要的多次重复计算。
2.尾递归
而尾递归就是一个函数中所有递归形式的调用都出现在函数的末尾。也就是说,程序每递归一次就算出相应的结果,函数调用出现在调用者函数的尾部, 没有必要去保存任何局部变量。尾递归函数的特点是在回归过程中不用做任何操作。
二、详细代码
当我们的问题时求阶层时:
1 * 2 * 3 * 4 … N
1.递归
代码如下(示例):
package main
import "fmt"
func Rescue(n int) int {
if n == 0 {
return 1
}
// 进入下一层
// 例:5 * Rescue(5-1)
// 再下一层
// 例:5 * 4 * Rescue(4-1)
return n * Rescue(n-1)
}
func main() {
fmt.Println(Rescue(5))
}
函数不断地调用本身,并且在下次调用时还会乘以一个变量n,这就是递归。在上面代码中,Rescue(5) 会不断调用自身,最终将自身改变为 5 * 4 * 3 * 2 * 1 * Rescue(0),由于程序在 n == 0 时 return ,此时 Rescue(0) = 0,那么最终结果就为120 。
因为递归式在调用自身下一层时使用了运算符相连,那么每次调用都都会使得运算符数量增多,,达到一定程度后,系统不得不使用栈进行数据保存和恢复。
如果每次递归都要运算一次所有运算符,那速度不仅极慢,还有可能导致栈溢出,导致程序奔溃。
2.尾递归
代码如下(示例):
package main
import "fmt"
func RescueTail(n int, a int) int {
if n == 1 {
return a
}
// 进入下一层
// 例:RescueTail(5-1, 1 * 5)
// 再下一层 此时 1 * 5 已计算完成
// 例:RescueTail(4-1, 5 * 4)
// 再下一层 此时 5 * 4 已计算完成
// 例:RescueTail(3-1, 20 * 3)
return RescueTail(n-1, a * n)
}
func main() {
fmt.Println(RescueTail(5, 1))
}
尾递归从上述代码可以知道递归函数在调用自身后直接传回其值,而不对其再加运算,效率将会极大的提高。与递归相比尾递归将 a * n 在 return 时已经计算,那么 每次调用只需要判断一次运算符,而不用考虑重复调用的累赘。在上面代码中,RescueTail(5, 1)虽然会不断调用自身,但最终形态依然为 RescueTail(0, 120),此时最终形态与初态差距不大,且由于程序在 n == 0 时 return ,此时 return a,那么最终结果就也为120 。
尾递归函数,部分IDE将会识别并进行优化,为尾递归分配更少的堆栈,相比递归来说减少了很多不必要的内存占用与降低了堆栈溢出风险。由于初态与最终态的形态相差不大,程序的堆栈不会进行大量占用与内存消耗,相比递归来说风险与实用性都大大提高。
总结
通过对比递归与尾递归,我们可以很轻松了解在Golang中尾递归的优势,那么熟悉尾递归,尽量减少递归写法是我们很需要的事情。在学习之路不断地提高,是我们永远的目标。
希望这个博客能对你有所益处。我是轻王,我为自己代言。