递归函数 #
一、递归概述 #
递归是函数直接或间接调用自身的过程。
1.1 递归要素 #
- 基准条件:终止递归的条件
- 递归条件:函数调用自身
1.2 基本结构 #
go
func recursive(n int) int {
if n <= 0 { // 基准条件
return 0
}
return n + recursive(n-1) // 递归条件
}
二、经典示例 #
2.1 阶乘 #
go
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1)
}
fmt.Println(factorial(5)) // 120
2.2 斐波那契数列 #
go
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
fmt.Println(fibonacci(10)) // 55
2.3 求和 #
go
func sum(n int) int {
if n <= 0 {
return 0
}
return n + sum(n-1)
}
fmt.Println(sum(100)) // 5050
2.4 幂运算 #
go
func power(x, n int) int {
if n == 0 {
return 1
}
return x * power(x, n-1)
}
fmt.Println(power(2, 10)) // 1024
三、递归应用 #
3.1 二分查找 #
go
func binarySearch(arr []int, target, low, high int) int {
if low > high {
return -1
}
mid := (low + high) / 2
if arr[mid] == target {
return mid
} else if arr[mid] > target {
return binarySearch(arr, target, low, mid-1)
} else {
return binarySearch(arr, target, mid+1, high)
}
}
arr := []int{1, 3, 5, 7, 9, 11, 13}
fmt.Println(binarySearch(arr, 7, 0, len(arr)-1)) // 3
3.2 汉诺塔 #
go
func hanoi(n int, from, to, aux string) {
if n == 1 {
fmt.Printf("Move disk 1 from %s to %s\n", from, to)
return
}
hanoi(n-1, from, aux, to)
fmt.Printf("Move disk %d from %s to %s\n", n, from, to)
hanoi(n-1, aux, to, from)
}
hanoi(3, "A", "C", "B")
3.3 全排列 #
go
func permute(arr []int, start int) {
if start == len(arr)-1 {
fmt.Println(arr)
return
}
for i := start; i < len(arr); i++ {
arr[start], arr[i] = arr[i], arr[start]
permute(arr, start+1)
arr[start], arr[i] = arr[i], arr[start]
}
}
permute([]int{1, 2, 3}, 0)
3.4 目录遍历 #
go
func walkDir(path string, indent string) error {
entries, err := os.ReadDir(path)
if err != nil {
return err
}
for _, entry := range entries {
fmt.Println(indent + entry.Name())
if entry.IsDir() {
walkDir(filepath.Join(path, entry.Name()), indent+" ")
}
}
return nil
}
walkDir(".", "")
3.5 快速排序 #
go
func quickSort(arr []int) []int {
if len(arr) <= 1 {
return arr
}
pivot := arr[0]
var left, right []int
for _, v := range arr[1:] {
if v < pivot {
left = append(left, v)
} else {
right = append(right, v)
}
}
result := append(quickSort(left), pivot)
result = append(result, quickSort(right)...)
return result
}
fmt.Println(quickSort([]int{3, 1, 4, 1, 5, 9, 2, 6}))
3.6 树遍历 #
go
type TreeNode struct {
Value int
Left *TreeNode
Right *TreeNode
}
func (n *TreeNode) PreOrder() {
if n == nil {
return
}
fmt.Println(n.Value)
n.Left.PreOrder()
n.Right.PreOrder()
}
func (n *TreeNode) InOrder() {
if n == nil {
return
}
n.Left.InOrder()
fmt.Println(n.Value)
n.Right.InOrder()
}
func (n *TreeNode) PostOrder() {
if n == nil {
return
}
n.Left.PostOrder()
n.Right.PostOrder()
fmt.Println(n.Value)
}
四、尾递归 #
4.1 概念 #
尾递归是递归调用是函数最后一步操作的递归。
4.2 普通递归 vs 尾递归 #
普通递归:
go
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1) // 递归后还有乘法
}
尾递归:
go
func factorialTail(n, acc int) int {
if n <= 1 {
return acc
}
return factorialTail(n-1, n*acc) // 递归是最后操作
}
factorialTail(5, 1) // 120
4.3 Go不支持尾递归优化 #
Go编译器不进行尾递归优化,尾递归仍会消耗栈空间。
五、递归优化 #
5.1 记忆化 #
缓存已计算结果:
go
var memo = make(map[int]int)
func fibonacciMemo(n int) int {
if n <= 1 {
return n
}
if v, ok := memo[n]; ok {
return v
}
memo[n] = fibonacciMemo(n-1) + fibonacciMemo(n-2)
return memo[n]
}
5.2 迭代替代 #
go
func factorialIter(n int) int {
result := 1
for i := 2; i <= n; i++ {
result *= i
}
return result
}
func fibonacciIter(n int) int {
if n <= 1 {
return n
}
a, b := 0, 1
for i := 2; i <= n; i++ {
a, b = b, a+b
}
return b
}
5.3 限制递归深度 #
go
func recursive(n, depth int) int {
if depth > 1000 {
panic("递归深度过大")
}
if n <= 0 {
return 0
}
return n + recursive(n-1, depth+1)
}
六、递归陷阱 #
6.1 栈溢出 #
go
func infinite(n int) int {
return infinite(n + 1) // 无基准条件,栈溢出
}
6.2 重复计算 #
go
func fibonacci(n int) int {
if n <= 1 {
return n
}
// fibonacci(n-1) 和 fibonacci(n-2) 有大量重复计算
return fibonacci(n-1) + fibonacci(n-2)
}
6.3 效率问题 #
递归通常比迭代慢:
go
// 递归
func sumRecursive(n int) int {
if n <= 0 {
return 0
}
return n + sumRecursive(n-1)
}
// 迭代(更快)
func sumIterative(n int) int {
sum := 0
for i := 1; i <= n; i++ {
sum += i
}
return sum
}
七、最佳实践 #
7.1 确保有基准条件 #
go
func recursive(n int) int {
if n <= 0 { // 基准条件
return 0
}
return n + recursive(n-1)
}
7.2 使用记忆化优化 #
go
var memo = make(map[int]int)
func fib(n int) int {
if n <= 1 {
return n
}
if v, ok := memo[n]; ok {
return v
}
memo[n] = fib(n-1) + fib(n-2)
return memo[n]
}
7.3 考虑迭代替代 #
go
// 对于简单问题,优先使用迭代
func factorial(n int) int {
result := 1
for i := 2; i <= n; i++ {
result *= i
}
return result
}
7.4 限制递归深度 #
go
const maxDepth = 1000
func recursive(n, depth int) int {
if depth > maxDepth {
return -1 // 或返回错误
}
// ...
}
八、总结 #
递归要点:
| 要素 | 说明 |
|---|---|
| 基准条件 | 终止递归的条件 |
| 递归条件 | 函数调用自身 |
| 尾递归 | 递归调用是最后操作 |
| 记忆化 | 缓存计算结果 |
关键点:
- 基准条件:必须有终止条件
- 栈空间:递归消耗栈空间
- 效率:递归可能比迭代慢
- 记忆化:优化重复计算
- 迭代替代:简单问题优先用迭代
准备好学习数组与切片了吗?让我们进入下一章!
最后更新:2026-03-26