递归函数 #

一、递归基础 #

1.1 什么是递归 #

递归是函数调用自身的编程技术。递归函数必须包含:

  1. 基准条件(终止条件)
  2. 递归调用
bash
#!/bin/bash

# 简单递归示例
countdown() {
    local n=$1
    
    # 基准条件
    if (( n <= 0 )); then
        echo "发射!"
        return
    fi
    
    echo "倒计时: $n"
    # 递归调用
    countdown $((n - 1))
}

countdown 5

1.2 递归执行过程 #

bash
#!/bin/bash

# 带调试信息的递归
factorial() {
    local n=$1
    local depth=${2:-0}
    local indent=$(printf '%*s' $((depth * 2)) '')
    
    echo "${indent}factorial($n) 开始"
    
    # 基准条件
    if (( n <= 1 )); then
        echo "${indent}factorial($n) 返回 1"
        echo 1
        return
    fi
    
    # 递归调用
    local sub_result=$(factorial $((n - 1)) $((depth + 1)))
    local result=$((n * sub_result))
    
    echo "${indent}factorial($n) 返回 $result"
    echo $result
}

result=$(factorial 4)
echo "最终结果: $result"

二、经典递归示例 #

2.1 阶乘 #

bash
#!/bin/bash

factorial() {
    local n=$1
    
    # 基准条件
    if (( n <= 1 )); then
        echo 1
        return
    fi
    
    # 递归计算
    local prev=$(factorial $((n - 1)))
    echo $((n * prev))
}

# 计算5的阶乘
result=$(factorial 5)
echo "5! = $result"  # 输出: 120

2.2 斐波那契数列 #

bash
#!/bin/bash

fibonacci() {
    local n=$1
    
    # 基准条件
    if (( n <= 0 )); then
        echo 0
        return
    fi
    
    if (( n == 1 )); then
        echo 1
        return
    fi
    
    # 递归计算
    local a=$(fibonacci $((n - 1)))
    local b=$(fibonacci $((n - 2)))
    echo $((a + b))
}

# 计算前10个斐波那契数
echo "斐波那契数列:"
for i in {0..10}; do
    echo -n "$(fibonacci $i) "
done
echo ""

2.3 求和 #

bash
#!/bin/bash

# 递归求和
sum() {
    local n=$1
    
    if (( n <= 0 )); then
        echo 0
        return
    fi
    
    local prev=$(sum $((n - 1)))
    echo $((n + prev))
}

result=$(sum 100)
echo "1到100的和: $result"

# 数组求和
array_sum() {
    local arr=("$@")
    local len=${#arr[@]}
    
    if (( len == 0 )); then
        echo 0
        return
    fi
    
    if (( len == 1 )); then
        echo "${arr[0]}"
        return
    fi
    
    local first=${arr[0]}
    local rest=("${arr[@]:1}")
    local sub_sum=$(array_sum "${rest[@]}")
    
    echo $((first + sub_sum))
}

numbers=(1 2 3 4 5)
result=$(array_sum "${numbers[@]}")
echo "数组求和: $result"

2.4 最大公约数 #

bash
#!/bin/bash

gcd() {
    local a=$1
    local b=$2
    
    # 基准条件
    if (( b == 0 )); then
        echo $a
        return
    fi
    
    # 递归调用(欧几里得算法)
    gcd $b $((a % b))
}

result=$(gcd 48 18)
echo "GCD(48, 18) = $result"

三、递归遍历 #

3.1 目录遍历 #

bash
#!/bin/bash

traverse_dir() {
    local dir="$1"
    local indent="${2:-}"
    
    # 检查目录
    if [[ ! -d "$dir" ]]; then
        return
    fi
    
    # 遍历目录内容
    for item in "$dir"/*; do
        local name=$(basename "$item")
        
        if [[ -d "$item" ]]; then
            echo "${indent}📁 $name/"
            traverse_dir "$item" "$indent  "
        elif [[ -f "$item" ]]; then
            echo "${indent}📄 $name"
        fi
    done
}

traverse_dir "/path/to/directory"

3.2 文件查找 #

bash
#!/bin/bash

find_files() {
    local dir="$1"
    local pattern="$2"
    
    # 检查目录
    if [[ ! -d "$dir" ]]; then
        return
    fi
    
    # 遍历目录
    for item in "$dir"/*; do
        if [[ -d "$item" ]]; then
            # 递归搜索子目录
            find_files "$item" "$pattern"
        elif [[ -f "$item" ]]; then
            # 检查文件名匹配
            local name=$(basename "$item")
            if [[ "$name" == *"$pattern"* ]]; then
                echo "$item"
            fi
        fi
    done
}

find_files "/home/user" "config"

3.3 JSON解析 #

bash
#!/bin/bash

parse_json_level() {
    local json="$1"
    local level="${2:-0}"
    local indent=$(printf '%*s' $((level * 2)) '')
    
    # 简单的JSON解析示例
    # 实际应用中应使用jq等工具
    
    if [[ "$json" =~ ^\{ ]]; then
        echo "${indent}对象:"
        # 递归解析对象内容
        # ...
    elif [[ "$json" =~ ^\[ ]]; then
        echo "${indent}数组:"
        # 递归解析数组内容
        # ...
    else
        echo "${indent}值: $json"
    fi
}

四、递归优化 #

4.1 尾递归优化 #

bash
#!/bin/bash

# 普通递归(非尾递归)
factorial() {
    local n=$1
    
    if (( n <= 1 )); then
        echo 1
        return
    fi
    
    local prev=$(factorial $((n - 1)))
    echo $((n * prev))  # 递归后还有计算
}

# 尾递归形式
factorial_tail() {
    local n=$1
    local acc="${2:-1}"
    
    if (( n <= 1 )); then
        echo $acc
        return
    fi
    
    # 尾递归调用(最后一步是递归)
    factorial_tail $((n - 1)) $((n * acc))
}

result=$(factorial_tail 5)
echo "5! = $result"

4.2 记忆化 #

bash
#!/bin/bash

# 使用全局数组缓存结果
declare -A fib_cache

fibonacci_memo() {
    local n=$1
    
    # 检查缓存
    if [[ -n "${fib_cache[$n]}" ]]; then
        echo "${fib_cache[$n]}"
        return
    fi
    
    # 基准条件
    if (( n <= 0 )); then
        fib_cache[0]=0
        echo 0
        return
    fi
    
    if (( n == 1 )); then
        fib_cache[1]=1
        echo 1
        return
    fi
    
    # 递归计算并缓存
    local a=$(fibonacci_memo $((n - 1)))
    local b=$(fibonacci_memo $((n - 2)))
    local result=$((a + b))
    
    fib_cache[$n]=$result
    echo $result
}

# 计算大数
result=$(fibonacci_memo 40)
echo "F(40) = $result"

4.3 迭代替代 #

bash
#!/bin/bash

# 递归版本
factorial_recursive() {
    local n=$1
    
    if (( n <= 1 )); then
        echo 1
        return
    fi
    
    local prev=$(factorial_recursive $((n - 1)))
    echo $((n * prev))
}

# 迭代版本(更高效)
factorial_iterative() {
    local n=$1
    local result=1
    
    for (( i=2; i<=n; i++ )); do
        ((result *= i))
    done
    
    echo $result
}

# 性能对比
time factorial_recursive 20
time factorial_iterative 20

五、递归实战 #

5.1 汉诺塔 #

bash
#!/bin/bash

hanoi() {
    local n=$1
    local from=$2
    local to=$3
    local aux=$4
    
    if (( n == 1 )); then
        echo "移动盘子 1 从 $from 到 $to"
        return
    fi
    
    hanoi $((n - 1)) "$from" "$aux" "$to"
    echo "移动盘子 $n 从 $from 到 $to"
    hanoi $((n - 1)) "$aux" "$to" "$from"
}

echo "汉诺塔问题(3个盘子):"
hanoi 3 "A" "C" "B"

5.2 二分查找 #

bash
#!/bin/bash

binary_search() {
    local -n arr=$1
    local target=$2
    local left=${3:-0}
    local right=${4:-$((${#arr[@]} - 1))}
    
    # 基准条件
    if (( left > right )); then
        echo -1  # 未找到
        return
    fi
    
    local mid=$(( (left + right) / 2 ))
    
    if (( arr[mid] == target )); then
        echo $mid
        return
    fi
    
    if (( arr[mid] > target )); then
        binary_search arr $target $left $((mid - 1))
    else
        binary_search arr $target $((mid + 1)) $right
    fi
}

# 测试
sorted_numbers=(1 3 5 7 9 11 13 15 17 19)
index=$(binary_search sorted_numbers 7)
echo "数字7的索引: $index"

5.3 快速排序 #

bash
#!/bin/bash

quicksort() {
    local -n arr=$1
    local left=${2:-0}
    local right=${3:-$((${#arr[@]} - 1))}
    
    if (( left >= right )); then
        return
    fi
    
    # 分区
    local pivot=${arr[$right]}
    local i=$((left - 1))
    
    for (( j=left; j<right; j++ )); do
        if (( arr[j] <= pivot )); then
            ((i++))
            # 交换
            local temp=${arr[$i]}
            arr[$i]=${arr[$j]}
            arr[$j]=$temp
        fi
    done
    
    # 放置pivot
    ((i++))
    local temp=${arr[$i]}
    arr[$i]=${arr[$right]}
    arr[$right]=$temp
    
    local pivot_index=$i
    
    # 递归排序
    quicksort arr $left $((pivot_index - 1))
    quicksort arr $((pivot_index + 1)) $right
}

# 测试
numbers=(5 2 8 1 9 3 7 4 6)
echo "排序前: ${numbers[@]}"
quicksort numbers
echo "排序后: ${numbers[@]}"

5.4 树遍历 #

bash
#!/bin/bash

# 使用关联数组表示树
declare -A tree_left
declare -A tree_right
declare -A tree_value

# 构建树
build_tree() {
    tree_value[1]="A"
    tree_left[1]=2
    tree_right[1]=3
    
    tree_value[2]="B"
    tree_left[2]=4
    tree_right[2]=5
    
    tree_value[3]="C"
    tree_left[3]=6
    tree_right[3]=7
    
    tree_value[4]="D"
    tree_value[5]="E"
    tree_value[6]="F"
    tree_value[7]="G"
}

# 前序遍历
preorder() {
    local node=$1
    
    if [[ -z "$node" || "$node" == "0" ]]; then
        return
    fi
    
    echo -n "${tree_value[$node]} "
    preorder "${tree_left[$node]}"
    preorder "${tree_right[$node]}"
}

# 中序遍历
inorder() {
    local node=$1
    
    if [[ -z "$node" || "$node" == "0" ]]; then
        return
    fi
    
    inorder "${tree_left[$node]}"
    echo -n "${tree_value[$node]} "
    inorder "${tree_right[$node]}"
}

# 后序遍历
postorder() {
    local node=$1
    
    if [[ -z "$node" || "$node" == "0" ]]; then
        return
    fi
    
    postorder "${tree_left[$node]}"
    postorder "${tree_right[$node]}"
    echo -n "${tree_value[$node]} "
}

# 测试
build_tree

echo "前序遍历:"
preorder 1
echo ""

echo "中序遍历:"
inorder 1
echo ""

echo "后序遍历:"
postorder 1
echo ""

六、递归注意事项 #

6.1 栈溢出 #

bash
#!/bin/bash

# 可能导致栈溢出
infinite_recursion() {
    infinite_recursion  # 无限递归
}

# 设置最大递归深度
MAX_DEPTH=100

safe_recursion() {
    local depth=${1:-0}
    
    if (( depth >= MAX_DEPTH )); then
        echo "达到最大递归深度"
        return 1
    fi
    
    safe_recursion $((depth + 1))
}

6.2 性能考虑 #

bash
#!/bin/bash

# 递归版本(较慢)
fib_recursive() {
    local n=$1
    (( n <= 1 )) && { echo $n; return; }
    echo $(( $(fib_recursive $((n-1))) + $(fib_recursive $((n-2))) ))
}

# 迭代版本(更快)
fib_iterative() {
    local n=$1
    local a=0 b=1
    
    for (( i=0; i<n; i++ )); do
        local temp=$((a + b))
        a=$b
        b=$temp
    done
    
    echo $a
}

# 对于大数,使用迭代
result=$(fib_iterative 40)
echo "F(40) = $result"

七、总结 #

7.1 递归要点 #

要点 说明
基准条件 必须有终止条件
递归调用 问题规模必须缩小
局部变量 使用local避免变量冲突
性能 考虑使用记忆化或迭代

7.2 下一步 #

你已经完成了函数部分的学习,接下来让我们学习 字符串基础,深入了解字符串处理!

最后更新:2026-03-27