【问题标题】:Can someone help me understand recursion?有人可以帮我理解递归吗?
【发布时间】:2013-12-10 16:58:11
【问题描述】:

所以我要在课堂上复习递归,我似乎无法理解它。有什么建议可以帮助描绘这个过程吗?

来自我正在做的示例测试:

class Q4
{
    public static void main(String[] args)
    {
        f(3);
    }
    public static void f(int x)
    {
        if (x > 0)
        {
            System.out.println(x);
            f(x-1);
            System.out.println(x);
            f(x-1);
        }
        System.out.println("bert");
    }
}

我看到了输出,但我不明白为什么它是输出。 谢谢

【问题讨论】:

    标签: java recursion


    【解决方案1】:

    考虑递归的一个好方法是从基本情况开始,然后看看当你一次应用递归步骤时会发生什么。

    基本情况:f(0)

    这里的基本情况是x <= 0f(0) 的输出是什么?我们可以立即看到这一点,因为从未输入过 if 语句。基本情况输出是:

    bert
    

    递归步骤:f(1)

    现在让我们看看f(1) 会发生什么。当x 为1 时,代码进入if 语句并最终调用f(0) 两次。如果将函数体中的1 替换为x,您会看到执行了以下语句:

    System.out.println(1);
    f(0);
    System.out.println(1);
    f(0);
    System.out.println("bert");
    

    println 语句的作用很明显,但是两个 f(0) 调用呢?好吧,我们知道f(0) 打印了什么,因为我们已经分析了基本情况。 f(0) 打印 bert。所以上面几行的输出是:

    1       // System.out.println(1);
    bert    // f(0);
    1       // System.out.println(1);
    bert    // f(0);
    bert    // System.out.println("bert");
    

    递归步骤:f(2)

    如果您对f(2) 应用相同的分析,您会看到它执行了:

    System.out.println(2);
    f(1);
    System.out.println(2);
    f(1);
    System.out.println("bert");
    

    如果我们在调用f(1) 的两个地方替换f(1) 的输出,我们得到:

    2       // System.out.println(2);
    1       // f(1);
    bert
    1
    bert
    bert
    2       // System.out.println(2);
    1       // f(1);
    bert
    1
    bert
    bert
    bert    // System.out.println("bert");
    

    递归步骤:f(3)

    最后,f(3) 执行:

    System.out.println(3);
    f(2);
    System.out.println(3);
    f(2);
    System.out.println("bert");
    

    代入f(2)的输出,我们得到:

    3       // System.out.println(3);
    2       // f(2);
    1       
    bert
    1
    bert
    bert
    2       
    1       
    bert
    1
    bert
    bert
    bert    
    3       // System.out.println(3);
    2       // f(2);
    1       
    bert
    1
    bert
    bert
    2       
    1       
    bert
    1
    bert
    bert
    bert    
    bert    // System.out.println("bert");
    

    【讨论】:

      【解决方案2】:

      通过查看fx,我们可以看到f 在这些情况下会做什么。

      f(3) 表示:

      System.out.println(3);
      f(2);
      System.out.println(3);
      f(2);
      System.out.println("bert");
      

      f(2) 表示:

      System.out.println(2);
      f(1);
      System.out.println(2);
      f(1);
      System.out.println("bert");
      

      f(1) 表示:

      System.out.println(1);
      f(0);
      System.out.println(1);
      f(0);
      System.out.println("bert");
      

      f(0) 表示:

      System.out.println("bert");
      

      因此,将所有内容放在一起意味着我们将交错输出递减的数字和"bert"。要查看每个号码或 "bert" 的来源,您需要逐步执行递归调用以查看发生了什么。

      例如,您将在最后以一行中的多个"bert" 字符串结尾,但这是因为每次调用f 都以打印"bert" 结尾。

      【讨论】:

        【解决方案3】:

        浏览纸上的代码。

        f(3):
            "3"
            f(3-1) = f(2):
                "2"
                 f(2-1) = f(1):
                      "1"
                      f(1-1) = f(0):
                           "bert"
                      "1"
                      f(1-1) = f(0):
                           "bert"
                 f(2-1) = f(1):
                      "1"
                       //...
        

        【讨论】:

          【解决方案4】:

          每次调用f(x-1) 时,都会为f 创建一个新的局部范围,并且一个名为x 的新局部变量仅在该局部范围内可用:

          调用f(3) 会创建一个新的局部作用域,其中局部变量x 被初始化为值3。我们称这个新的局部作用域为LS1。单步执行这个方法,可以看到3 > 0为真,所以方法打印出3(先调用System.out(x)

          然后该方法调用自身,但传递一个 x-1 的值。 JVM 在这里做的第一件事是计算 x-1,也就是 2。注意,它不会将结果分配给x;我们称为LS1 的范围内的变量x 仍然是3。

          它的作用是调用 f(2)。这将创建一个带有名为 x 的局部变量的新局部范围。让我们称这个新的本地范围为LS2。在 LS2 中,我们无法从 LS1 访问任何变量。 LS2 有自己的一组局部变量 - 为 LS2 分配的内存中的新块与 LS1 不同。 LS2 中的局部变量 x 现在初始化为 2。

          再一次,我们现在可以单步执行f 以跟随流程。系统打印出2,然后计算x-1(等于1)并调用f(1)。同样,在调用f(1) 时会创建一个新的本地范围(我们称之为LS3),并为其局部变量分配另一个新的内存块。

          LS3 中 x 的值被初始化为 1,然后该方法继续。它打印出 1,然后调用 f(0)。这将创建一个新的局部范围(我们称之为LS4),并为其局部变量分配了一个新的内存块。 LS4 中的 x 初始化为 0。单步执行 f,我们发现 0 > 0 为 false,因此该代码块被忽略。打印出bert,方法退出。

          局部作用域LS4 现在被销毁,它的内存块(包含它的局部变量)被释放回堆。控制权现在回落到我们称为LS3 的本地范围内。回过头来,我们可以看到LS3中变量x的最后一个值是1。下一条指令说打印出来,所以打印出1,然后打印出bert然后退出方法。

          现在退出会破坏我们称为LS3 的本地范围。控制流回落到我们称为LS2 的本地范围内。 LS2 中的变量x 被设置为2,所以2 被打印出来,然后是bert

          方法退出,销毁LS2 并退回到LS1LS1 中的x3,所以打印了3,然后是bert。方法退出,程序结束。

          希望一切都有意义!

          编辑

          对不起,我基本上错过了第二次打给f(x-1) 的电话,那时同样的事情又发生了;一个新的范围被创建并且该方法重复出现。

          【讨论】:

            【解决方案5】:

            参见Recursion 是一个方法调用,其中同一个方法调用自身。
            所以在你的代码中:

             public static void f(int x)
                {
                    if (x > 0)
                    {
                        System.out.println(x);
                        f(x-1);    //at this point it will call itself as f(2)
                        System.out.println(x);
                        f(x-1);
                    }
                    System.out.println("bert");
                }
            

            它将继续调用自己,直到 if 条件为真。您需要了解的主要是方法调用的堆栈情况,无论它是否递归。

            【讨论】:

              【解决方案6】:
              private String returnType(int t, String[] s) <- return type recursion example
              {
                  String arrayed = s[t];
                  if (t == 0) // <--if 0
                      return " " + arrayed;
                  else
                      return arrayed + returnType(t - 1, s); <--if != 0 return value and then call itself again with the value -1.
              }
              

              到0时返回arrayed,通过String[],把所有的字符串并排放在一个字符串中

              【讨论】:

                猜你喜欢
                • 2021-12-24
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-07-23
                相关资源
                最近更新 更多