递归是一种基于分而治之思维的解决问题的方法。
基本思想是你把原始问题分成更小的(更容易解决的)实例,解决那些更小的实例(通常再次使用相同的算法),然后将它们重新组合成最终的解决方案。
典型示例是生成 n 的阶乘的例程。 n 的阶乘是通过将 1 和 n 之间的所有数字相乘来计算的。 C# 中的迭代解决方案如下所示:
public int Fact(int n)
{
int fact = 1;
for( int i = 2; i <= n; i++)
{
fact = fact * i;
}
return fact;
}
迭代解决方案并不令人惊讶,任何熟悉 C# 的人都应该理解它。
通过识别第 n 个阶乘是 n * Fact(n-1) 来找到递归解决方案。或者换一种说法,如果你知道一个特定的阶乘数是什么,你就可以计算下一个阶乘数。这是 C# 中的递归解决方案:
public int FactRec(int n)
{
if( n < 2 )
{
return 1;
}
return n * FactRec( n - 1 );
}
这个函数的第一部分被称为Base Case(或者有时是Guard Clause),它可以防止算法永远运行。每当使用 1 或更小的值调用函数时,它只会返回值 1。第二部分更有趣,被称为递归步骤。在这里,我们使用稍微修改的参数调用相同的方法(我们将其减 1),然后将结果与我们的 n 副本相乘。
第一次遇到这种情况时可能会让人感到困惑,因此在运行时检查它的工作原理会很有启发性。假设我们调用 FactRec(5)。我们进入了例程,没有被基本情况所接受,所以我们最终是这样的:
// In FactRec(5)
return 5 * FactRec( 5 - 1 );
// which is
return 5 * FactRec(4);
如果我们重新输入带有参数 4 的方法,我们又不会被保护子句停止,所以我们最终会:
// In FactRec(4)
return 4 * FactRec(3);
如果我们把这个返回值代入上面的返回值,我们得到
// In FactRec(5)
return 5 * (4 * FactRec(3));
这应该会为您提供有关如何得出最终解决方案的线索,以便我们快速跟踪并展示下降过程中的每一步:
return 5 * (4 * FactRec(3));
return 5 * (4 * (3 * FactRec(2)));
return 5 * (4 * (3 * (2 * FactRec(1))));
return 5 * (4 * (3 * (2 * (1))));
当基本情况被触发时,会发生最终的替换。在这一点上,我们有一个简单的代数公式来求解,它首先直接等同于阶乘的定义。
请注意,对方法的每次调用都会导致触发基本情况或调用参数更接近基本情况的同一方法(通常称为递归调用)。如果不是这种情况,那么该方法将永远运行。