当前位置: 首页 > 编程笔记 >

Java中的迭代和递归详解

白晋鹏
2023-03-14
本文向大家介绍Java中的迭代和递归详解,包括了Java中的迭代和递归详解的使用技巧和注意事项,需要的朋友参考一下

前言

最近在看书的时候看到这一内容,感觉还是蛮有收获的。迭代使用的是循环(for,while,do...wile)或者迭代器,当循环条件不满足时退出。而递归,一般是函数递归,可以是自身调用自身,也可以是非直接调用,即方法A调用方法B,而方法B反过来调用方法A,递归退出的条件为if,else语句,当条件符合基的时候退出。

上面是迭代和递归的语法特性,他们在Java中有什么不同呢?下面通过这篇文章来详细了解了解。

一、递归

提到迭代,不得不提一个数学表达式: 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的值成正比的。但是,如果我们站在程序的角度,考虑他们是如何运行的话,那么这两个算法就有很大不同了。

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

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

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

      递归中一定有迭代,但是迭代中不一定有递归,大部分可以相互转换。

      能用迭代的不要用递归,递归调用函数不仅浪费空间,如果递归太深的话还容易造成堆栈的溢出。

四、数形递归

前面介绍过,树递归随输入的增长的信息量呈指数级增长。比较典型的就是斐波那契数列:

用文字描述就是斐波那契数列中前两个数字的和等于第三个数字:0,1,1,2,3,5,8,13,21……

递归实现代码如下:

int fib (int n) {
 if (n == 0) {
  return 0;
 } else if (n == 1) {
  return 1;
 } else {
  return fib(n-1) + fib(n-2);
 }
}

计算过程中,为了计算fib(5) ,程序要先计算fib(4) 和 fib(3) ,要想计算fib(4) ,程序同样需要先计算 fib(3) 和 fib(2) 。在这个过程中计算了两次fib(3)。

从上面分析的计算html" target="_blank">过程可以得出一个结论:使用递归实现斐波那契数列存在冗余计算。

就像上面提到的,可以用递归的算法一般都能用迭代实现,斐波那契数列的计算也一样。

int fib (int n) {
 int fib = 0;
 int a = 1;
 for(int i=0; i<n; i++) {
  int temp = fib;
  fib = fib + a;
  a = temp;
 }
 return fib;
}

虽然使用递归的方式会有冗余计算,可以用迭代来代替。但是这并不表明递归可以完全被取代。因为递归有更好的可读性。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者使用Java能有所帮助,如果有疑问大家可以留言交流。

 类似资料:
  • 问题内容: 我正在编写一个递归函数,其目的是迭代pList文件。我的代码是 但是当我调用函数“ HashMapper((Map)((Map)entry).keySet());”时。我有一个例外 java.util.HashMap $ HashMap条目不能转换为java.util.Map 我不知道如何调用函数以及如何将Hashmap条目转换为Map 问题答案: 入境确实不是。它是,因此您可以根据需

  • 如果说在任何地方都使用递归,那么可以使用for循环,对吗?如果递归通常比较慢,那么将其用于循环迭代的技术原因是什么? 如果总是可以将递归转换为for循环,那么有经验法则吗?

  • 计算机的威力源自其反复执行同一任务或同一任务不同版本的能力。在计算领域,迭代这一主题会以多种形式出现。数据模型中的很多概念(比如表)都是某种形式的重复,比如“表要么为空,要么由一个元素接一个元素,再接一个元素,如此往复而成”。使用迭代,程序和算法可以在不需要单独指定大量相似步骤的情况下,执行重复性的任务,如“执行下一步骤1000次”。编程语言使用像C语言中的while语句和for语句那样的循环结构

  • 前面几节介绍了两个可以方便地用递归与迭代实现的函数。本节要比较递归与迭代方法,介绍为什么程序员在不同情况下选择不同方法。 递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。递归与迭代都涉及重复:迭代显式使用重复结构,而递归通过重复函数调用实现重复。递归与迭代都涉及终止测试:迭代在循环条件失败时终止,递归在遇到基本情况时终止。使用计数器控制重复的迭代和递归都逐渐到达终止点:迭代一直修改计

  • 我在scheme中构建了一个递归函数,它将在一些输入上重复给定的函数f,n次。 我需要用尾递归构建这个函数的迭代版本,如果我正确理解尾递归,我认为我做得对。 我的问题是,这真的是迭代的吗?我相信我已经使用尾部递归正确地构建了它,但从技术上讲,它仍然将一系列操作推迟到count=0,在这里,它执行叠加的任意多个组合。

  • 问题内容: 前几天,我以为我在jQuery中看到了一个对象迭代器,该对象迭代器具有可以设置为对子对象进行递归迭代的标志。我以为它是jQuery.each()的一部分,但是现在我在文档中看不到该功能。 jQuery中是否有任何此类迭代器可以自动递归? (我知道如何用JavaScript进行操作。只是想知道我是否确实看到了我以为看到的东西。) 非常感谢! 编辑: 要清楚,我在想像jQuery.each