Java中的递归
什么是递归?
函数直接或间接调用自身的过程称为递归,相应的函数称为递归函数。使用递归算法,可以很容易地解决某些问题。此类问题的示例是河内塔 (TOH)、中序/前序/后序树遍历、图的 DFS 等。
递归的基本条件是什么?
在递归程序中,提供了基本情况的解决方案,并以较小的问题来表示较大问题的解决方案。
int fact(int n)
{
if (n < = 1) // base case
return 1;
else
return n*fact(n-1);
}
在上面的示例中,定义了 n < = 1 的基本情况,并且可以通过转换为较小的值来解决较大的数字值,直到达到基本情况。
如何使用递归解决特定问题?
这个想法是用一个或多个较小的问题来表示一个问题,并添加一个或多个停止递归的基本条件。例如,如果我们知道 (n-1) 的阶乘,我们计算阶乘 n。阶乘的基本情况是 n = 0。当 n = 0 时我们返回 1。
为什么递归时会出现 Stack Overflow 错误?
如果未达到或未定义基本情况,则可能会出现堆栈溢出问题。让我们举个例子来理解这一点。
int fact(int n)
{
// wrong base case (it may cause
// stack overflow).
if (n == 100)
return 1;
else
return n*fact(n-1);
}
如果调用 fact(10),它将调用 fact(9)、fact(8)、fact(7) 等,但数量永远不会达到 100。因此,基本情况未达到。如果堆栈上的这些函数耗尽了内存,则会导致堆栈溢出错误。
直接递归和间接递归有什么区别?
如果函数fun 调用相同的函数fun,则称为直接递归函数。如果函数fun 调用另一个函数fun_new 和 fun_new 直接或间接调用 fun,则称为间接递归。表 1 说明了直接递归和间接递归之间的区别。
- 直接递归:
void directRecFun() { // Some code.... directRecFun(); // Some code... }
- 间接递归:
void indirectRecFun1() { // Some code... indirectRecFun2(); // Some code... } void indirectRecFun2() { // Some code... indirectRecFun1(); // Some code... }
有尾递归和无尾递归有什么区别?
当递归调用是函数执行的最后一件事时,递归函数是尾递归的。有关详细信息,请参阅尾递归文章。
如何在递归中将内存分配给不同的函数调用?
当从 main() 调用任何函数时,内存会在堆栈上分配给它。递归函数调用自身,被调用函数的内存分配在分配给调用函数的内存之上,并且为每个函数调用创建不同的局部变量副本。当达到基本情况时,函数将其值返回给调用它的函数,并释放内存并继续处理。
让我们通过一个简单的函数来举例说明递归是如何工作的。
// A Java program to demonstrate
// working of recursion
class GFG {
static void printFun(int test)
{
if (test < 1)
return;
else {
System.out.printf("%d ", test);
// Statement 2
printFun(test - 1);
System.out.printf("%d ", test);
return;
}
}
public static void main(String[] args)
{
int test = 3;
printFun(test);
}
}
3 2 1 1 2 3
当从 main() 调用printFun(3)时,会为printFun(3)分配内存,并将局部变量 test 初始化为 3,并将语句 1 到 4 压入堆栈,如下图所示。它首先打印“3”。在语句 2 中,调用 printFun(2)并将内存分配给printFun(2)并将局部变量 test 初始化为 2 并将语句 1 到 4 压入堆栈。类似地, printFun(2)调用printFun(1)和printFun(1)调用printFun(0) 。 printFun(0)转到 if 语句并返回printFun(1) 。执行printFun(1)的其余语句,然后返回到printFun(2)等等。在输出中,打印从 3 到 1 的值,然后打印 1 到 3。内存堆栈如下图所示。
递归编程相对于迭代编程的缺点是什么?
请注意,递归程序和迭代程序都具有相同的解决问题的能力,即每个递归程序都可以迭代地编写,反之亦然。递归程序比迭代程序具有更大的空间需求,因为所有函数都将保留在堆栈中,直到达到基本情况。由于函数调用和返回开销,它还具有更大的时间要求。
递归编程相对于迭代编程的优势是什么?
递归提供了一种简洁明了的代码编写方式。有些问题本质上是递归的,如树遍历、河内塔等。对于此类问题,最好编写递归代码。我们也可以在堆栈数据结构的帮助下迭代地编写这样的代码。例如参考无递归的中序树遍历,河内迭代塔。