C语言函数递归

C语言函数递归

什么是递归

递归是函数直接或间接调用自身的一种编程技巧,通常用于将问题分解为相似的子问题。

关键点:

基准条件(Base Case):终止递归的条件,防止无限调用。递归条件(Recursive Case):函数调用自身,逐步向基准条件逼近。

示例:阶乘

int factorial(int n) {

if (n == 0) return 1; // 基准条件

else return n * factorial(n - 1); // 递归条件

}

注意:

递归可能引发栈溢出(如深度过大)。某些问题(如斐波那契数列)用递归效率低,可改用迭代或记忆化优化。

递归的限制条件

递归的限制条件主要包括以下几点:

1. 基准条件(Base Case)

必须至少有一个明确的终止条件,否则递归会无限进行,导致栈溢出。示例:if (n == 0) return 1; // 阶乘递归的基准条件

2. 递归深度限制(Stack Overflow)

每次递归调用都会占用栈空间,深度过大会导致 栈溢出(Stack Overflow)。C语言默认栈空间有限(通常几MB),不适合过深递归(如数万层)。

3. 效率问题

递归可能重复计算(如斐波那契数列的朴素递归),时间复杂度高。某些情况可用 迭代(循环) 或 记忆化(缓存结果) 优化。

4. 间接递归需谨慎

若函数A调用B,B又调用A(间接递归),需确保最终能终止。

示例(错误递归,无基准条件):

void infinite_recursion() {

infinite_recursion(); // 无限调用,导致栈溢出

}

总结:递归必须满足 基准条件 + 有限深度,否则可能崩溃或低效。

递归的常见举例

递归在C语言中常用于解决可分解为相似子问题的情况。以下是几个经典示例:

1. 阶乘(Factorial)

公式:n! = n × (n-1)!,基准条件 0! = 1

int factorial(int n) {

if (n == 0) return 1; // 基准条件

else return n * factorial(n - 1); // 递归调用

}

2. 斐波那契数列(Fibonacci)

公式:fib(n) = fib(n-1) + fib(n-2),基准条件 fib(0)=0, fib(1)=1

int fib(int n) {

if (n <= 1) return n; // 基准条件

else return fib(n - 1) + fib(n - 2); // 递归调用(效率低,可优化)

}

注意:朴素递归会重复计算,实际应用需用 动态规划 或 迭代 优化。

3. 汉诺塔(Tower of Hanoi)

规则:将n个盘子从A柱移到C柱,每次只能移动一个盘子,且大盘不能压小盘。

void hanoi(int n, char from, char to, char aux) {

if (n == 1) {

printf("Move disk 1 from %c to %c\n", from, to); // 基准条件

} else {

hanoi(n - 1, from, aux, to); // 将n-1个盘子移到辅助柱

printf("Move disk %d from %c to %c\n", n, from, to);

hanoi(n - 1, aux, to, from); // 将n-1个盘子移到目标柱

}

}

4. 链表递归遍历

递归打印链表节点:

typedef struct Node {

int data;

struct Node* next;

} Node;

void printList(Node* node) {

if (node == NULL) return; // 基准条件

printf("%d ", node->data);

printList(node->next); // 递归调用

}

5. 二分查找(递归版)

前提:数组已排序。

int binarySearch(int arr[], int left, int right, int target) {

if (left > right) return -1; // 基准条件:未找到

int mid = left + (right - left) / 2;

if (arr[mid] == target) return mid; // 找到目标

else if (arr[mid] > target)

return binarySearch(arr, left, mid - 1, target); // 递归左半

else

return binarySearch(arr, mid + 1, right, target); // 递归右半

}

6. 计算数组和

int arraySum(int arr[], int n) {

if (n <= 0) return 0; // 基准条件

return arr[n - 1] + arraySum(arr, n - 1); // 递归求和

}

递归 vs 迭代的选择

递归:代码简洁,适合问题天然递归(如树、分治算法)。迭代:避免栈溢出,效率更高(如斐波那契数列改用循环)。

关键:确保递归有终止条件,且问题规模逐步减小!

递归与迭代

递归(Recursion)与迭代(Iteration)对比

递归和迭代都是重复执行代码的方式,但实现机制不同,适用于不同场景。

特性递归(Recursion)迭代(Iteration)基本思想函数调用自身,分解问题为更小的子问题使用循环结构(for/while)重复执行代码块终止条件必须定义明确的基准条件(Base Case),否则无限递归依赖循环条件,条件不满足时退出循环内存消耗每次递归调用占用栈空间,可能栈溢出(Stack Overflow)仅需固定内存,无额外函数调用开销代码可读性更简洁,接近数学定义(如阶乘、斐波那契)通常更冗长,但逻辑直接适用场景问题天然可递归分解(如树、分治、回溯)线性问题(如遍历数组)、需避免栈溢出的情况性能可能因重复计算(如斐波那契)或函数调用开销较慢通常更快,无额外函数调用开销

经典示例对比

1. 计算阶乘

递归实现

int factorial(int n) {

if (n == 0) return 1; // 基准条件

return n * factorial(n - 1); // 递归调用

}

迭代实现

int factorial(int n) {

int result = 1;

for (int i = 1; i <= n; i++)

result *= i;

return result;

}

2. 斐波那契数列

递归实现(效率低,O(2ⁿ))

int fib(int n) {

if (n <= 1) return n;

return fib(n - 1) + fib(n - 2); // 重复计算严重

}

迭代实现(高效,O(n))

int fib(int n) {

if (n <= 1) return n;

int a = 0, b = 1, c;

for (int i = 2; i <= n; i++) {

c = a + b;

a = b;

b = c;

}

return b;

}

如何选择?

用递归:

问题可自然分解为子问题(如汉诺塔、二叉树遍历)。代码简洁性比性能更重要(如快速排序、归并排序)。 用迭代:

需要避免栈溢出(如计算大数阶乘)。性能敏感场景(如斐波那契数列)。问题适合线性处理(如数组求和、查找)。

递归转迭代的技巧

递归通常可用 栈(Stack) 模拟调用过程转为迭代:

// 递归的深度优先搜索(DFS)转迭代(使用显式栈)

void dfs_iterative(Node* root) {

Stack stack;

stack.push(root);

while (!stack.empty()) {

Node* node = stack.pop();

process(node);

if (node->right) stack.push(node->right);

if (node->left) stack.push(node->left);

}

}

总结:

递归适合“分治”问题,迭代适合“线性”问题。递归需警惕栈溢出,迭代需注意循环条件。两者可相互转换,但各有最佳适用场景。

相关文章

岩谷气具(珠海)有限公司
365bet官网是什么

岩谷气具(珠海)有限公司

07-10 8359
塞尔达传说荒野之息马怎么登记地点
365bet安卓手机客户端

塞尔达传说荒野之息马怎么登记地点

08-12 2441
高德地图怎么模拟导航
365体育比分官网

高德地图怎么模拟导航

09-25 4455
超威和天能哪个电池贵?
365体育比分官网

超威和天能哪个电池贵?

08-16 8095
天使形象在西方艺术史中的演变
365bet安卓手机客户端

天使形象在西方艺术史中的演变

07-21 5237
手机记录宝宝出生天数app大全排行
365bet官网是什么

手机记录宝宝出生天数app大全排行

08-21 4510
只狼月隐糖在哪里及获取方法
365bet官网是什么

只狼月隐糖在哪里及获取方法

08-30 3804