java 递归程序实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/neweastsun/article/details/83961799

java 递归程序实现

本文我们介绍编程语言的一个核心概念————递归。介绍递归功能特性,以及如何使用递归解决不能类型问题。

1. 理解递归

1.1. 递归定义

java中函数调用机制支持方法可以调用自身,这种功能称为递归。举例,我们计算求和函数:

public int sum(int n) {
    if (n >= 1) {
        return sum(n - 1) + n;
    }
    return n;
}

递归函数有两个主要前提:

  • 终止条件——当一定条件满足时,函数返回特定值,不再递归调用
  • 递归调用——函数调用自身,其输入值更接近终止条件

每次递归调用都会在jvm栈内存空间中增加栈帧,因此我们不注意递归深度,可能会发生栈内存溢出错误。 我们可以利用尾递归优化进行规避。

1.2. 尾递归VS头递归

当递归调用是执行函数的最后部分则称为尾递归,反之为头递归。我们现在通过尾递归方式实现上面的示例:

public int tailSum(int currentSum, int n) {
    if (n <= 1) {
        return currentSum + n;
    }
    return tailSum(currentSum + n, n - 1);
}

使用尾递归,递归调用是执行方法中最后需要执行的内容,在当前方法中不再有其他内容需要去执行。因此,理论上无需存储当前函数的栈帧。所以编译器能利用这点优化内存,但java编译器暂时没有针对尾递归进行优化。

1.3. 递归VS迭代

递归可以帮助简化解决负责问题,且代码更简洁、可读。但通常需要更多的栈内存以及增加每次递归调用。作为替代方案,能使用递归解决的问题,也可以使用迭代方式实现。下面通过迭代方式实现上面求和的示例:

public int iterativeSum(int n) {
    int sum = 0;
    if(n < 0) {
        return -1;
    }
    for(int i=0; i<=n; i++) {
        sum += i;
    }
    return sum;
}

相比于递归,迭代方法性能更好。但迭代更复杂,相比于递归更难理解。例如:遍历二叉树时两种方法对比很明显。

在头递归、尾递归以及迭代方法中选择最佳方案,取决于特定问题和情况。

2. 示例实战

下面我们通过示例使用递归解决几个问题。

2.1. 求10的n次方

假如我们需要求10 的n次方。输入为n。通过递归方式实现。首先技术10 的 (n-1)次方,然后乘以10。然后计算10 的 (n-1)次方,即为10 的 (n-2)次方乘以10,以此类推,直到计算(n-n)次方,值为1。java实现代码如下:

public int powerOf10(int n) {
    if (n == 0) {
        return 1;
    }
    return powerOf10(n-1) * 10;
}

2.2. 求斐波那契数列的第n个元素值

从0和1开始,斐波那契数列中每个元素是前两个元素之和:0,1,1,2,3,5,8,13,21,34……
所以,给定数值n,我们的问题是求第n个元素。使用递归方案,我们需要考虑终止条件以及递归调用。
还好,两种都非常简单。求n个元素值,只要先求f(n) = f(n-1) + f(n-2) (递归调用)。同时 f(0) = 0 和 f(1) = 1 (终止条件)。那么定义递归方法就比较简单:

public int fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
    return fibonacci(n-1) + fibonacci(n-2);
}

2.3 十进制转二进制

下面我们通过递归方法实现十进制转二进制问题。需求是输入十进制数n,输出二进制字符串。
方法为十进制数除以2,记录余数继续用商除以2。一直除直到商为0.然后使用倒叙写出所有余数,就获得结果。
因此,我们的问题是写一个方法按照相反的顺序返回这些余数:

public String toBinary(int n) {
    if (n <= 1 ) {
        return String.valueOf(n);
    }
    return toBinary(n / 2) + String.valueOf(n % 2);
}

2.4 计算二叉树高度

二叉树高度定义为从根到最深叶子节点的边数。我们的问题是计算给定二叉树的高度值。
一个简单方法是查找最深的叶子,然后计算其到根的边数。使用递归的方案实现,我们重新描述二叉树高度定义————求二叉树中左分支和右分支中最大的高度,然后加一。如果根没有左分支和右分支,其高度为0.

代码实现为:

public int calculateTreeHeight(BinaryNode root){
    if (root!= null) {
        if (root.getLeft() != null || root.getRight() != null) {
            return 1 + 
              max(calculateTreeHeight(root.left), 
                calculateTreeHeight(root.right));
        }
    }
    return 0;
}

通过示例,我们看到通过递归方式解决一些问题非常简单。

3. 总结

本文我们介绍了java中递归的概念,并演示了几个简单示例。

猜你喜欢

转载自blog.csdn.net/neweastsun/article/details/83961799