【问题标题】:Recursion in Java How does it work? [duplicate]Java中的递归它是如何工作的? [复制]
【发布时间】:2016-02-20 08:12:18
【问题描述】:

请在下面的代码中解释递归语句的工作原理。

int factR(int n) {
    int result;

    if(n==1) return 1;

    result = factR(n-1) * n;
    return result;
}

我的理解是:

在上述语句中,factR(n-1) 方法调用自身直到结束。假设我们想要获得 6 的阶乘,它将作为参数发送到此方法。它将作为参数n 接收,然后检查n 的值;如果为 1,则返回 1。但如果它不是 1,比如我们的例子中它是 6,那么递归语句就会运行。

现在我面临的问题是,第一次n-1 变为 5 并乘以 n,保持值为 6,然后变为 30。现在 30 会去哪里?

然后该方法将调用自身,这一次n-1 变为 4,然后乘以 n,其中 IF 持有值“6”,然后 4*6=24 我认为这是错误的。因为如果我们通过这种方式,那么在下一次通话中 该过程将类似于,n-1 将变为 3*n,其中 IF 保持相同的值,即 6,然后它将变为 3*6= 18。然后发生下一次调用,如果我们相乘并假设 n-1 变为 2 987654330@ 保留值 6,然后 2*6= 12,最后调用 n-1= 1 * n = 6。我的意思是,很明显 n-1 将减小值 n-1 即 6-1= 5 然后 5-1=4 然后 4-1=3 然后 3-1=2 和 2-1=1。但问题是n 的值是多少,每次方法调用自身时都会相乘?

如果您说当第一次乘法发生时,即“n-1”变为 5,然后乘以 6 = 30,并且 30 存储在“n”处,那么在下一次调用中 5-1=4*30=120,然后是 4-1=3*120=360 ,然后是 3-1=2*360=720 ,最后是 1*720=720 那么Java如何确定将结果值放回变量n

如果我放置另一个语句来检查变量result每次以这种方式调用自身时的值是多少,如下所示:

int factR(int n) { 
    int result; 

    if(n==1) return 1; 

    result = factR(n-1)*n ;
    System.out.println(result);
    return result; 
}

然后我得到这个输出:

2
6
24
120
720
Factorial of 6 is 720

我不明白它是如何在第一次调用中产生 2 的。值 2 和 6、24、120 和 720 是从哪里来的?我认为我严重卡在代码的工作中。

【问题讨论】:

  • 你试过调试代码了吗?
  • 你走错路了。在你的情况下,它不会首先得到30。它将以1*2 开头-> 将结果传递给调用者方法*3 -> 将结果传递给调用者方法*4 等等。递归调用的每个调用都被放入堆栈,当它到达不再进行任何进一步递归的点时,它会从顶部(最后一次调用)一直回到底部(第一次调用)。
  • 2 不是第一次调用,是第二次调用,只是你的第一次调用在 println 之前返回
  • 因为:1) 你有 if(n==1) return 1;在您的 Syste.out.println 之前,这意味着不打印第一次调用 2)您的 System.out.println 是在递归调用之后,这意味着将首先调用递归,然后输出 ==> 输出将是“从头到尾”。 Soooo:跳过第一个调用(1),第二个是 1+2=2,第三个是 1*2*3 = 6 等等
  • if(n==1) return 1; 是一个错误 条件:factR(0) 挂起系统。应该是if(n <= 1) return 1;

标签: java recursion


【解决方案1】:

在 java 中,我们有一个叫做 stack 的东西。

每当一个方法被另一个方法调用时,它就会被添加到堆栈中。

 ________
|factR(1)| = <prints nothing>
 ________
|factR(2)| = 2
 ________
|factR(3)| = 6
 ________
|factR(4)| = 24
 ________
|factR(5)| = 120
 ________
|factR(6)| = 720

这基本上意味着factR(6)方法要完成,factR(5)必须完成,factR(5)要完成,factR(4)必须完成等等。

factR(6) 中调用factR(5) 然后等待它完成,因为结果取决于它。

但是,在factR(5) 中,您也进行了递归调用,您必须等待。

以此类推,直到我们达到限制 factR(1),它只会返回 1。

factR(1) 完成后,factR(2) 可以打印出结果并将结果返回给其父方法,依此类推,直到factR(6) 打印出并返回其结果

【讨论】:

  • bruno_cw ,在阅读了 Mukesh Kumar 的上述回答后,您的回答真的很容易理解,坦率地说,它就像清晰的词本身一样清晰 :-) ,希望知识分享你这么漂亮的方式,谢谢
  • 哇,非常感谢! :)
【解决方案2】:

函数扩展直到到达终止语句 (n == 1)。所以假设n = 6,那么我们有factR(n-1) * n = factR(5) * 6,但factR(5)是什么?好吧,它只是factR(4) * 5,所以我们看到了factR(5) * 6 = (factR(4) * 5) * 6。现在注意factR(1) = 1,我们得到

factR(6) = factR(5) * 6 
         = (factR(4) * 5) * 6 
         = ((factR(3) * 4) * 5) * 6 
         = (((factR(2) * 3) * 4) * 5) * 6 
         = ((((factR(1) * 2) * 3) * 4) * 5) * 6 
         = ((((1 * 2) * 3) * 4) * 5) * 6
         = (((2 * 3) * 4) * 5) * 6
         = ((6 * 4) * 5) * 6
         = (24 * 5) * 6
         = 120 * 6
         = 720

【讨论】:

    【解决方案3】:

    您可能在这里遗漏的是 n 是您的函数的局部变量。这意味着对您的函数的每次调用(无论是否通过递归)都会获得一个新变量n,其中包含该函数的参数。因为它是一个值类型,所以它被复制而不是对原始值的引用。因此,在一次调用中对其进行的任何更改都不会影响其他(递归)调用中的变量。

    因此,您的函数首先获得6 的副本,并将减少1 作为副本提供给您的函数的下一次调用。该调用获得6-1=5 的“副本”并再次减少它 - 依此类推。当它到达1 时,它也返回1。然后它再次通过调用堆栈向上工作,并将最后一次调用的结果与本次调用中的局部变量相乘。所以12 相乘并返回。该结果与3 相乘,以此类推。最后你得到了阶乘。

    【讨论】:

      【解决方案4】:

      该方法真的应该像这样构造以找到n的阶乘:

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

      在我看来,在递归函数内实例化新变量并不是一个好主意,因为它们会因为作用域而在每次调用时简单地重置。

      【讨论】:

      • 这和他的方法完全一样,只是代码更少,只是他能够用打印语句调试他的代码
      • @KevinEsche 是的,我知道,我个人认为实例化最终会由于递归而超出范围的变量是不好的做法;增加了更多的混乱。
      • 这并不能真正回答问题
      猜你喜欢
      • 1970-01-01
      • 2017-01-18
      • 2017-03-19
      • 2012-08-31
      • 1970-01-01
      • 2013-04-19
      • 2013-10-28
      • 1970-01-01
      • 2019-10-02
      相关资源
      最近更新 更多