问题引入
斐波那且数列 : 1 1 2 3 5 8 13 21 ... (从第三项开始的数等于前面两个数之和)
代码实现
func fibo(n int) int {
if n <= 2 {
return 1
} else {
return fibo(n-1) + fibo(n-2)
}
}
分析
假如求fibo(10), 则会要先求fibo(9)和fibo(8),然后求fibo(9)则先要求fibo(8)和fibo(7),
同理,求fibo(8)则先要求fibo(7)和fibo(6)... 依此类推(充斥着大量的重复计算)。
因此,求第n项的值,时间复杂度为 2^n。
优化方向
优化方向一:由于充斥着大量的重复计算,如果能加入缓存把已经计算过的存起来,则可以大大减少计算量。以空间换取时间,大大减少执行时间
var arr = make([]int, 50) // 全局变量,切片 用于加入缓存
func fibo1(n int) int {
if n <= 2 {
return 1
}
if arr[n] > 0 { // 先检查值是否已经计算过,如果计算过,则直接返回
return arr[n]
} else {
res := fibo1(n-1) + fibo1(n-2)
arr = append(arr, res) // 如果没有计算过,则加入数组缓存
return res
}
}
优化方向二:任何递归可以解决的事情,都可以使用循环解决,将时间复杂度由2^n将为O(n),效率大大提升
func fibo3(n int) int {
var c int // 相当于计算总和
var a int = 1 // 相当于fibo1(n-1)
var b int = 1 // 相当于fibo1(n-2)
if n <= 2 {
return 1
} else {
for i := 3; i <= n; i++ { // 从第三项开始
c = a + b // 相当于 fibo1(n-1) + fibo1(n-2)
a = b
b = c
}
return c
}
}
优化方向三:尾递归方式解决(尾递归就是将上一次的计算结果传递到下一次计算当中)
/**
* pre 上一次计算结果
* res 当前结果
* n 项
*/
func fibo2(pre int, res int, n int) int {
if n <= 2 {
return res
} else {
return fibo2(res, pre+res, n-1)
}
}
另外一个例子,求 n!
递归方式 尾递归
func mul(n int, res int) int {
if n <= 1 {
return res
} else {
res = n * res
return mul(n-1, res)
}
}
循环方式
//计算阶乘 n! 非递归方式
func noRecMul(n int) int {
if n <= 1 {
return 1
} else {
res := 1
for i := n; i > 0; i-- {
res = res * i
}
return res
}
}
总结
虽然递归可以让代码看起来更加通俗易懂,但是其时间复杂度是存在极大的优化空间的,因此如果在项目中遇到
类似问题,可以从以上几个方向去优化