Java中的迭代与递归

原文出处:http://www.hollischuang.com/archives/1298

递归

提到迭代,不得不提一个数学表达式: n!=n*(n-1)(n-2)…*1

有很多方法来计算阶乘。有一定数学基础的人都知道n!=n*(n-1)!因此,代码的实现可以直接写成:

代码一

int factorial (int n) {
    if (n == 1) {
        return 1;
    } else {
        return n*factorial(n-1);
    }
}   

在执行以上代码的时候,其实机器是要执行一系列乘法的: factorial(n) → factorial(n-1) → factorial(n-2) → … → factorial(1)。所以,需要不断的跟踪(跟踪上次计算的结果)并调用乘法进行计算(构建一个乘法链)。这类不断调用自身的运算形式称之为递归。递归可以进一步的分为线性递归和数形递归。信息量随着算法的输入呈线性增长的递归称之为线性递归。计算n!(阶乘)就是线性递归。因为随着N的增大,计算所需的时间呈线性增长。另外一种信息量随着输入的增长而进行指数增长的称之为树形递归。

迭代

另外一种计算n!的方式是:先计算1乘以2,然后用其结果乘以3,再用的到的结果乘以4….一直乘到N。在程序实现时,可以定义一个计数器,每进行一次乘法,计数器都自增一次,直到计数器的值等于N截至。代码如下:

代码二

int factorial (int n) {
    int product = 1;
    for(int i=2; i<n; i++) {
        product *= i;
    }
    return product;
}

和代码一相比,代码二没有构建一个乘法链。在进行每一步计算时,只需要知道当前结果(product)和i的值就可以了。这种计算形式称之为迭代。迭代有这样几个条件:1、有一个有初始值的变量。2、一个说明变量值如何更新的规则。3、一个结束条件。(循环三要素:循环变量、循环体和循环终止条件)。和递归一样。时间要求随着输入的增长呈线性的可以叫做线性迭代。

迭代 VS 递归

比较了两个程序,我们可以发现,他们看起来几乎相同,特别是其数学函数方面。在计算n!的时候,他们的计算步数都是和n的值成正比的。但是,如果我们站在程序的角度,考虑他们是如何运行的话,那么这两个算法就有很大不同了。

(注:原文中关于其区别写的有点扯,这里就不翻译了,下面是笔者自己总结内容。)

首先分析递归,其实递归最大的有点就是把一个复杂的算法分解成若干相同的可重复的步骤。所以,使用递归实现一个计算逻辑往往只需要很短的代码就能解决,并且这样的代码也比较容易理解。但是,递归就意味着大量的函数调用。函数调用的局部状态之所以用栈来记录的。所以,这样就可能浪费大量的空间,如果递归太深的话还有可能导致堆栈溢出。

接下来分析迭代。其实,递归都可以用迭代来代替。但是相对于递归的简单易懂,迭代就比较生硬难懂了。尤其是遇到一个比较复杂的场景的时候。但是,代码的难以理解带来的有点也比较明显。迭代的效率比递归要高,并且在空间消耗上也比较小。

**

递归中一定有迭代,但是迭代中不一定有递归,大部分可以相互转换。
能用迭代的不要用递归,递归调用函数不仅浪费空间,如果递归太深的话还容易造成堆栈的溢出。

**

### Java 中使用迭代方法替代递归的实现 在 Java 编程中,当需要通过迭代方式代替递归时,通常会借助显式的栈结构来模拟递归过程。这种方法的核心在于手动管理调用栈,从而避免隐式递归带来的潜在性能开销或栈溢出风险。 #### 使用迭代法重写快速排序算法 以下是一个基于栈的迭代版本的快速排序实现: ```java import java.util.Stack; public class IterativeQuickSort { public static void iterativeQuickSort(int[] arr) { if (arr == null || arr.length <= 1) return; Stack<Integer> stack = new Stack<>(); int low = 0, high = arr.length - 1; stack.push(low); stack.push(high); while (!stack.isEmpty()) { high = stack.pop(); low = stack.pop(); if (low >= high) continue; int pIndex = partition(arr, low, high); // 获取分区索引 // 将左子数组压入栈 if (pIndex - 1 > low) { stack.push(low); stack.push(pIndex - 1); } // 将右子数组压入栈 if (pIndex + 1 < high) { stack.push(pIndex + 1); stack.push(high); } } } private static int partition(int[] arr, int low, int high) { int pivot = arr[high]; int i = low - 1; for (int j = low; j < high; j++) { if (arr[j] <= pivot) { i++; swap(arr, i, j); } } swap(arr, i + 1, high); return i + 1; } private static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } public static void main(String[] args) { int[] unsortedArray = {10, 7, 8, 9, 1, 5}; iterativeQuickSort(unsortedArray); System.out.println("Sorted array : " + java.util.Arrays.toString(unsortedArray)); // 输出已排序数组 [^2] } } ``` 上述代码展示了如何利用 `Stack` 数据结构来模拟递归调用的过程[^1]。每次执行一次划分操作后,将两个待处理区间分别加入到栈中以便后续继续处理。 --- #### 阶乘问题的迭代解决方案 对于经典的阶乘问题,也可以采用类似的思路将其转换为非递归形式: ```java public class FactorialIteration { public static long factorialIterative(int n) { if (n < 0) throw new IllegalArgumentException("Negative input not allowed."); long result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; } public static void main(String[] args) { int number = 5; System.out.println(number + "! = " + factorialIterative(number)); } } ``` 此方案无需依赖函数自身的重复调用即可完成计算任务[^4]。 --- #### 总结 无论是复杂的数据结构操作还是简单的数值运算,都可以找到相应的迭代替代策略。这种技术不仅适用于像快速排序这样的经典分治算法,也广泛应用于其他场景下消除不必要的递归调用所带来的额外负担。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值