递归三要素
- 一个问题得解,可以分解为几个子问题的解。
- 这个问题与分解之后的子问题,除了数据规模不同,求解思路要完全一样。
- 存在递归终止条件。
递归实现步骤
- 根据把大问题分解为小问题得规律,找出递推公式,找到终止条件。
- 翻译成代码。
举例
假设有n个台阶,一步可以走1个台阶或者2个台阶,问走完n个台阶有多少种走法?
package main
import "fmt"
func Step(n int) (sum int) {
if n <= 2 {
return n
}
sum = Step(n-1) + Step(n-2)
return
}
func main() {
sum := Step(4)
fmt.Println(sum)
}
注意点
堆栈溢出
原因:递归的函数调用的临时变量都存储在内存栈中,等函数执行完成返回时候才出栈,当调用次数过多,层次太深时,就造成堆栈溢出。
解决方式:可以添加全局计数,限制递归深度,触达最大深度时,直接终止退出程序,只是个饮鸩止渴方式,并不能实际解决问题。
package main
import "fmt"
var i int
func Step(n int) (sum int) {
if i == 100 {
panic("stack over flow .")
}
i++
if n <= 2 {
return n
}
sum = Step(n-1) + Step(n-2)
return
}
func main() {
sum := Step(100)
fmt.Println(sum)
}
避免重复计算
递归到一定小规模数据解时候,事先离线计算把小规模解计算好,存储在一个列表表中,当递归时,先读取散列表有没有对应规模的解,有了直接取值返回,避免重复计算。
package main
import "fmt"
var i int
var HasSolvedList = make(map[int]int)
func Step(n int) (sum int) {
if i == 100000 {
panic("stack over flow .")
}
i++
if v, ok := HasSolvedList[n]; ok {
//fmt.Printf("HasSolvedList n : %d\n", n)
return v
}
if n <= 2 {
return n
}
sum = Step(n-1) + Step(n-2)
return
}
func main() {
HasSolvedList[2] = 2
HasSolvedList[3] = 3
HasSolvedList[4] = 5
HasSolvedList[5] = 8
sum := Step(20)
fmt.Println(sum)
}
函数调用耗时多
函数调用空间复杂度高
复杂度
todo