Java 中不同类型的递归

函数或方法调用自身的过程称为递归。递归是 Java 中的突出主题之一。在本教程中,我们将讨论 Java 中不同类型的递归。

递归类型
递归主要有两种类型:

1)直接递归
直接递归意味着方法直接调用自身。直接递归可以进一步分为五个部分:

a) 尾递归:在尾递归中,函数调用自身,负责递归的语句是方法的最后一条语句。也就是说,在递归语句之后,不能再写任何语句。如果递归函数正在调用自身,并且该递归调用是函数中的最后一个语句,则称为尾递归。该函数在调用时必须执行或处理任何操作,并且在返回时不执行任何操作。


// 演示尾递归的 Java 程序  ;
public class TailRecursion  
{  
  
// 递归函数  ;
static void functn(int y)  
{  
if (y > 0)  
{  
System.out.println(
"The value is " + y);  
  
// 实现递归  ;
// 它是方法的最后一句;
functn(y - 1);  
}  
}  
  
// main method  
public static void main(String[] argvs)  
{  
int y = 4;  
functn(y);  
}  
}  

输出:
The value is 4
The value is 3
The value is 2
The value is 1


复杂性分析:程序的时间复杂度为 O(n)。这是因为负责递归的语句运行了 n 次。程序的空间复杂度为 O(n)。

使用循环
让我们看看使用循环做同样的事情会发生什么。

// Java program that demonstrates tail recursion  
public class TailRecursion1  
{  
  
// function that uses iteration  
static void functn(int y)  
{  
for(; y > 0; y--)  
{  
System.out.println(
"The value is " + y);  
  
}  
}  
  
// main method  
public static void main(String[] argvs)  
{  
int y = 4;  
functn(y);  
}  
}  

输出:
The value is 4
The value is 3
The value is 2
The value is 1

复杂性分析:程序的时间复杂度为 O(n)。这是因为循环运行了 n 次。程序的空间复杂度为 O(1)。

通过观察这两个程序可以发现,迭代(使用循环)的性能要优于递归,因为迭代占用的空间更少。让我们来探究一下其中的原因。

  • 在循环的情况下,当函数"(void functn(int y)) "执行时,堆栈中只创建了一条激活记录(变量 "y "的激活记录)。因此,栈内只创建了一个内存单位,使得程序的空间复杂度为 O(1)。
  • 与此相反,在递归的情况下,每次函数调用时都会创建一个单独的激活记录,从而使程序的空间复杂度达到 O(n),前提是递归持续 n 次。

b) 头递归:在头部递归中,负责递归的语句是函数的第一条语句。换句话说,任何有关操作的语句都不应出现在负责递归的语句之前。

// Java program that demonstrates head recursion  
public class HeadRecursion  
{  
  
// Recursion function  
static void functn(int y)  
{  
if (y > 0)  
{  
// 实现递归  ;
// 它是方法的第一句;
functn(y - 1);  
System.out.println(
"The value is " + y);  
  
  
}  
}  
  
// main method  
public static void main(String[] argvs)  
{  
int y = 4;  
functn(y);  
}  
}  
输出:
The value is 1
The value is 2
The value is 3
The value is 4

复杂性分析:程序的时间复杂度为 O(n),程序的空间复杂度也是 O(n)。

c) 线性递归:当一个函数调用自身,但只调用一次时,称为线性递归。观察它的伪代码。

funtn(t)  
{  
    // some code can be written here  
    if(t > 0)  
    {  
        fun(t - 1);
// the function is calling itself only single time  
    }  
   
// some code can be written here   
}  

在头部递归和尾部递归中讨论过的程序也是线性递归的例子。

d) 树递归:当一个函数不止一次调用自身时,就称为树递归。请看下面的例子。

// Java program that demonstrates tree recursion  
public class TreeRecursion  
{  
  
// Recursion function  
static void functn(int y)  
{  
if (y > 0)  
{  
System.out.println(
"The value is " + y);  
functn(y - 1);
// 函数首次调用自身  ;
  
functn(y - 1);
// 函数第二次调用自身  
}  
}  
  
// main method  
public static void main(String[] argvs)  
{  
int y = 4;  
functn(y);  
}  
}  

输出:

The value is 4
The value is 3
The value is 2
The value is 1
The value is 1
The value is 2
The value is 1
The value is 1
The value is 3
The value is 2
The value is 1
The value is 1
The value is 2
The value is 1
The value is 1

复杂性分析:程序的时间复杂度为 O(2n),空间复杂度也是 O(2n)。时间和空间复杂度都是指数级的,因为一次递归调用会导致两次单独的递归调用。

e) 嵌套递归:在这种递归中,函数会调用自身,负责递归的语句是嵌套的,这意味着在递归调用的参数内部有一个递归调用。换句话说,就是 "递归内部有递归"。下面的示例将使这一概念更加清晰。

// Java program that demonstrates tree recursion  
public class NestedRecursion  
{  
  
// Recursion function  
static int functn(int y)  
{  
// the base case  
if(y == 0)  
{  
    return 0;  
}  
  
System.out.println(
"The value is " + y);  
  
// 递归调用内的递归调用  ;
return functn(functn(y - 1));   
  
}  
  
// main method  
public static void main(String[] argvs)  
{  
int y = 4;  
int a = functn(y);  
}  
}  

输出:

The value is 4
The value is 3
The value is 2
The value is 1

复杂性分析:程序的时间复杂度为 O(n),程序的空间复杂度也是 O(n)。

2) 间接递归
假设有一个函数叫做 funA(),这个函数正在调用另一个函数 funB(),这个函数正在调用另一个函数 funC(),而 funC() 函数正在调用函数 funA(),那么这种类型的递归叫做间接递归。我们看到函数 funA() 调用函数 funB(),函数 funB() 调用函数 funC(),函数 funC() 调用函数 funA()。让我们看看它的实现。

// Java program that demonstrates indirect recursion  
public class IndirectRecursion  
{  
  
// Recursion function  
static void funA(int y)  
{  
if (y > 0)  
{  
System.out.println(
"In function A, the value is " + y);  
  
// invoking function funB()  
funB(y - 1);  
}  
  
}  
  
  
// Recursion function  
static void funB(int y)  
{  
if (y > 0)  
{  
System.out.println(
"In function B, the value is " + y);  
  
// invoking function funC()  
funC(y - 1);  
}  
  
}  
  
// Recursion function  
static void funC(int y)  
{  
if (y > 0)  
{  
System.out.println(
"In function C, the value is " + y);  
// invoking function funA()  
funA(y - 1);  
}  
}  
// main method  
public static void main(String[] argvs)  
{  
int y = 10;  
funA(y);  
}  
}  

输出:

In function A, the value is 10
In function B, the value is 9
In function C, the value is 8
In function A, the value is 7
In function B, the value is 6
In function C, the value is 5
In function A, the value is 4
In function B, the value is 3
In function C, the value is 2
In function A, the value is 1

复杂性分析:程序的时间复杂度为 O(n),程序的空间复杂度也是 O(n)。