目录
1.【递归:从概念到实践】
1.1 什么是递归?
- 在数学和计算机科学中,递归是指一个函数不断调用自身的过程。递归函数通常通过不断将问题分解为更小的、类似的子问题来解决复杂的任务。
- 递归的核心思想是将大问题分解为小问题,直到达到基本情况(递归的终止条件),然后逐层返回结果。
1.2 递归的原理
递归函数通常包括两部分:基本情况和递归情况。基本情况是指递归过程中能够立即得到解答的情况,它是递归的终止条件。递归情况则是指在函数内部调用自身来解决规模更小的子问题。
递归函数的执行过程可以用以下步骤来描述:
- 检查是否满足基本情况,如果是,则返回特定的值。
- 如果不满足基本情况,将问题分解为更小的子问题,然后调用自身来解决这些子问题。
- 逐层返回结果,最终得到整个问题的解答。
1.3 递归的经典例子:阶乘
让我们以计算阶乘为例来演示递归的实际应用。阶乘表示从1到n的所有正整数的乘积,通常用符号n!表示。阶乘的递归定义如下:
- 当n为1时,n! = 1
- 当n大于1时,n! = n * (n-1)!
下面是用C语言实现阶乘递归的代码:
#include <stdio.h>
int dg(int n)
{
if (n == 1)
{
return 1; // 基本情况
}
else
{
return n * dg(n - 1); // 递归情况
}
}
int main()
{
int n = 5;
int x = dg(n);
printf("%d的阶乘是%d\n", n, x);
return 0;
}
在这个例子中,factorial
函数通过递归调用自身来计算阶乘。当n为1时,函数返回1作为基本情况;当n大于1时,函数调用自身来计算(n-1)的阶乘,并将结果乘以n,最终得到n的阶乘。
1.3.1 代码分析
让我们来分析一下这个函数的执行过程:
当调用 dg
(n)
时,函数首先检查基本情况:如果 n 等于 0 或 1,那么它的阶乘就是 1,于是函数直接返回 1。如果 n 不等于 0 或 1,那么函数执行
return n *
dg(n-1)
。这里就是递归的关键:函数调用了自身,并且传入了一个比原来小的数(n-1)。这个过程会一直持续下去,直到 n 等于 0 或 1,然后逐层返回结果,直到得到最终的阶乘值。
让我们以一个具体的例子来说明这个过程。假设我们要计算dg
(5)
:
- dg
(5)
返回 5 * dg(4)
- dg
(4)
返回 4 * dg(3)
- dg
(3)
返回 3 * dg(2)
- dg
(2)
返回 2 * dg(1)
- dg
(1)
返回 1然后逐层返回结果:
- dg 返回 2 * 1 = 2
- dg 返回 3 * 2 = 6
- dg 返回 4 * 6 = 24
- dg 返回 5 * 24 = 120
因此,递归的执行过程更像是一种"等待"的过程,每次函数调用都需要等待它所调用的函数返回结果,然后才能继续执行。只有当递归调用到达基本情况时,才会开始逐层返回结果,最终得到整个问题的解答
1.4 递归溢出的问题
当处理递归函数时,我们通常会面临函数递归溢出的问题。递归溢出是指在使用递归算法时,递归的层次过深,导致系统栈空间不足,从而抛出栈溢出异常。下面我将使用 JavaScript 代码来说明递归函数的问题以及解决方法。
#include <stdio.h>
// 递归函数计算斐波那契数列
int dh(int n)
{
if (n <= 1)
{ // 终止条件
return n;
}
else
{
return dh(n - 1) + dh(n - 2); // 递归调用
}
}
// 调用递归函数
int main()
{
int result = dh(40); // 尝试计算斐波那契数列第40项
printf("%d\n", result);
return 0;
}
在上述代码中,我们定义了一个计算斐波那契数列的递归函数 dh
。然而,如果我们尝试计算较大项数的斐波那契数列,比如 dh(40)
,就会遇到栈溢出的问题。
为了解决这个问题,我们可以使用尾递归优化或者循环来改写递归函数,以减少系统栈的压力。
下面是使用尾递归优化的计算斐波那契数列的函数:
#include <stdio.h>
// 尾递归优化的计算斐波那契数列函数
int dh_tail(int n, int a, int b)
{
if (n == 0)
{ // 终止条件
return a;
}
return dh_tail(n - 1, b, a + b); // 尾递归调用
}
// 调用尾递归优化的函数
int main()
{
int result = dh_tail(40, 0, 1); // 计算斐波那契数列第40项
printf("%d\n", result);
return 0;
}
另外,我们也可以使用循环的方式来计算斐波那契数列,从而避免递归调用的深度过深:
#include <stdio.h>
// 循环计算斐波那契数列的函数
int dh_iterative(int n) {
int a = 0, b = 1, temp;
if (n == 0) {
return a;
}
for (int i = 2; i <= n; i++) {
temp = a + b;
a = b;
b = temp;
}
return b;
}
// 调用循环函数
int main() {
int result = dh_iterative(40); // 计算斐波那契数列第40项
printf("%d\n", result);
return 0;
}