【问题标题】:Method calling itself.. recursive?方法调用自己..递归?
【发布时间】:2014-03-02 18:22:36
【问题描述】:
public static int m(int i, int j)    
{     
  if ( i > j)    
    return 0;    
  else    
  {
    i++;    
    m(i++, j);
  }
  return i;    
}

我有两个问题。 1.) out.print(m(3,8)); 返回什么和 2.) 方法 m 被调用了多少次?答案应分别为 5 和 7。

当我解决问题 1 时,我得到了 5,但我这样做的方式不正确,因为该方法没有被调用 7 次,它只被调用了两次。我这样做的方式是我直接进入 else 语句,因为 (i > j) 在开始时是错误的,这次又用 (4, 8) 调用了方法 m 我认为它仍然是错误的,所以我回到 m 所在的行由于m(i++, j) 中的i++ 被调用,变量 i 更改为 5。之后它将返回 5 作为 i 的值。

这显然是错误的,所以我在整个程序中为 i 的值添加了一些 out.prints,以查看值是如何变化的,它从 3 变为 9,并在方法 m 的开头使用 out.print(i);。在return i; 之前的out.print(i); 显示值开始从10 倒退到5,并且该方法被调用了7 次。这是如何工作的?

编辑:记录后,我能够想出一些逻辑,但我希望有人澄清它是正确的。

方法 m 以 3,8 开头。之后,它用 4,8 然后 5,8....直到 9,8 调用自身因为 m(i++, j) 是 post(i) 然后 i 变成 10,然后返回 10,然后是 9,然后是 8,然后是 7、6,最后是 5。当它返回 10 时,即 1,9 是 2、8是 3,7 是 4,6 是 5,5 是 6。因此,当 i = 5 时,它是 6,这就是返回的值。它是否正确?如果是的话,最好有更深入的解释。

【问题讨论】:

标签: java recursion methods call


【解决方案1】:

我会在一般情况下分析函数,具体的参数值只会在最后使用。当你有这样的工具时,运行这个函数并通过调试器或调试打印观察它的作用是很方便的,但在某些情况下你必须只依靠你的大脑。例如。从FPGA 提取调试信息真的很难(你需要模拟它的工作)。在接受工作面试时,您通常会使用计算机来测试代码——您的分析技能正在接受测试。这就是为什么我强烈建议在查看代码在执行时的实际作用之前使用铅笔和纸的方法。

问题一:返回值

在尝试分析复杂的代码时,知道可以忽略什么是成功的关键。

你知道的

  • 代码是单线程的,
  • 没有全局变量,对当前调用之外的世界没有副作用,
  • 没有使用递归调用的返回值。

因此,无需考虑其他线程可能会造成什么混乱,您可以分析单个调用而无需考虑递归调用将如何影响它(除了返回值),并且您可以忘记递归调用,除非它是无限递归(不会让你的程序终止),因为它除了消耗时间之外没有任何影响。

递归不是无限的,因为i 总是在递归调用之前递增,并且在i > j 时递归停止。

知道了这一点,决定返回值是很容易的。函数可以简化为

public static int m(int i, int j)
{
    if (i > j)
        return 0;
    else
        i += 2;
    return i;
}

因为return会终止函数的执行,所以可以进一步简化为

public static int m(int i, int j)
{
    return (i > j) ? 0 : i + 2;
}

给你问题1的答案。当调用m(3, 8)时,结果是i + 2,即5,因为i小于j

问题 2:调用次数

递归是线性的——每次调用最多进行一次递归调用。所以你必须计算在到达递归底部之前需要多少次调用。

递归的底部是条件的第一个分支。因此,您计算在i > j 之前拨打了多少次电话。

j 在每个调用中都有相同的值。没有命令改变它的值,它总是被原封不动地传递给递归调用。 i 总是在递归调用之前和调用之后递增一次(i++ 是后递增,在使用原始值后生效)。只有第一个增量与递归调用相关。

为了计算递归调用,函数可以简化为

public static void m(int i, int j)
{
    if (i > j)
        return;
    else
        m(i + 1, j);
}

由此可见i连续加1,直到大于j

m(3, 8),调用是

  • m(3, 8),
  • m(4, 8),
  • m(5, 8),
  • m(6, 8),
  • m(7, 8),
  • m(8, 8)
  • m(9, 8).

所以有 7 个。

如果给定的参数有较大的差异,一般的解决方案会很方便。所以让我们来探索一下。会很快的。

如果一开始是i > j,显然只打了一个电话。否则……从i 数到j + 1 时,你遇到了多少个数字? (j + 1) - i + 1 = j - i + 2。加一是用于最上面的电话。这是一般的答案。

【讨论】:

    【解决方案2】:

    您看到值递减的原因是因为在您打印最后一个“i”之前,此值仅在本地范围内递增(else 条件中的第一个 i++)。

    当你的 m 函数返回给它的调用者时,i 不再是 i+1,因为它在孩子中,因此你会看到递减的值,直到根 'm' 调用被返回。

    【讨论】:

      【解决方案3】:

      为了更好地理解正在发生的事情,这样重构代码可能会有所帮助:

      public static int m(int i, int j) {
          static int calls = 0;     
          System.out.println("entering.  count: " + calls);
          calls++;
      
          System.out.println("i = " + i);
          System.out.println("j = " + j);
      
          if (i > j) { 
              System.out.println("returning 0");
              return 0;
          } else {
              i++;    
              m(i++, j);
          }
      
          System.out.println("returning " + i);
          return i;    
      }
      

      请注意,我没有修改任何实际逻辑。我所做的只是添加一些语句来打印值,并添加一个变量来跟踪方法被调用的次数。

      【讨论】:

      • 我对你的代码做了一些修改,但它的逻辑相同,我只是在调用时遇到了问题,它们都为零,我不知道为什么但是我通过将 m 更改为 (int i, int j, int calls) 并将 out.print(m(3,8)) 更改为 (3,8,1) 来解决此问题,因为第一次调用就在那里。现在让我感到困惑的是,从我看到的只有一个“i++”在增加 i 的值。我以为它会去 i,j 3,8 4,8 6,8..但它有 5,8 虽然我不知道为什么
      • 我刚刚注意到两个i++ 电话。你当然可以在i++;m(i++,j); 之间再打一个System.out.println("i = " + i);。我的回答的重点确实是:如有疑问,请记录很多......(或使用断点)。
      猜你喜欢
      • 1970-01-01
      • 2019-06-28
      • 1970-01-01
      • 1970-01-01
      • 2015-04-21
      • 2023-04-10
      • 1970-01-01
      • 2016-03-03
      相关资源
      最近更新 更多