【问题标题】:Trouble Understanding Recursion难以理解递归
【发布时间】:2014-01-31 16:02:07
【问题描述】:

好的,我有一个程序:

public class Rec {
    public static void main(String[] args) {
        test(5);
    }
    static void test(int n) {
        if (n > 0) {
        System.out.println(n);
        test(n-1);
        System.out.println(n);
        }
    }

它的输出是 5,4,3,2,1,1,2,3,4,5。我的问题是,为什么/如何执行第二个 println(n) 语句?我认为函数调用会完全切断它,相反它的行为方式让我感到困惑。这不是作业或任何东西,我真的很难理解递归是如何工作的。

【问题讨论】:

  • 你不认为函数调用永远不会返回吗?
  • 使用调试器单步调试代码,您会看到。或者只是用铅笔和纸来解决。
  • 铅笔和纸+1;我发现它经常有帮助。
  • 我不知道如何使用调试器,从来没有人教过我。在铅笔和纸上解决它给我留下了答案 54321
  • 不要忘记函数返回后的代码仍然会被执行

标签: java recursion


【解决方案1】:

所有方法调用完成后都会返回到同一个地方。

通过有效地将它们链接起来

 System.out.println(5)
     System.out.println(4)
        System.out.println(3)
            System.out.println(2)
                system.out.println(1)
                   // If is not true now.
                System.out.println(1)
            System.out.println(2)
        System.out.println(3)
     System.out.println(4)
 System.out.println(5)

这有意义吗?

【讨论】:

    【解决方案2】:

    当您的 test 方法终止时,它将在上面堆栈帧中的相同位置恢复。

    例如,如果我有一些代码:

    public class Test {
        public static void main(String[] args) {
            System.out.println("A");
            myFunc();
            System.out.println("B");
        }
    
        public static void myFunc() {
            System.out.println("Do something here");
        }
    }
    

    ...您希望 main 中的两个 println 都运行,因为 myFunc 终止/不会进入无限循环。

    这是真的,即使您使用的是递归。您的 test 方法最终将终止,这意味着必须在某个时间点执行第二个 println。


    相比之下,假设我们有一个永不终止的“递归”函数:

    public class Test {
        public static void main(String[] args) {
            test2(5)
        }
    
        public static void test2(int n) {
            System.out.println("A " + n);
            test(n - 1);
            System.out.println("B " + n);
        }
    }
    

    因为test2 方法永远不会终止,所以第二个 println 绝对无法执行。正是出于这个原因,您应该始终设计任何递归函数,以便它可以在达到某些条件时终止。

    【讨论】:

      【解决方案3】:

      堆栈的概念对于理解递归非常重要。堆栈是一种 LIFO 数据结构。请记住,只要发生方法调用,当前状态就会被压入堆栈(当前状态涉及局部变量的值、下一个可执行语句的地址等)。现在让我们看看你的问题。

      首先发生这种情况,

      System.out.println(5);
      test(n-1); // method call is happening so store state to stack !!!
      //stack contents: n=5 and address of next statement
      System.out.println(4);
      test(n-1);//another state added to stack : n =4
      System.out.println(3);
      test(n-1);//another state added to stack: n = 3
      System.out.println(2);
      test(n-1);//another state added to stack : n = 2
      System.out.println(1); 
      test(n-1);//another state added to stack : n = 1
      

      现在条件 if(n>0) 失败,现在返回递归阶段发生,即控制返回到进行调用的状态,并记住所有状态都存储在stack 并且还记得 stack 是 LIFO 所以现在:

      // n = 1 first state in stack
      System.out.println(1); 
      //n = 2 second state stored in stack
      System.out.println(2); 
      //n = 3 third state stored in stack
      System.out.println(3);
      //n = 4 fourth state stored in the stack
      System.out.println(4); 
      //n = 5 last state stored in stack 
      System.out.println(5); 
      

      现在所有的调用都完成了,控制返回到main。

      请看你的代码:

      static void test(int n) {
              if (n > 0) {
              System.out.println(n);
              test(n-1);// this makes the new call to method test() and causes the state to be stored in the stack
              System.out.println(n);// this statement doesn't execute until the recursive call made to test() doesn't return .
              }
      

      HTH :)

      【讨论】:

        【解决方案4】:

        假设你的预期结果应该是“54321”,你的问题是你的第二个

        System.out.println(n);
        

        你的测试方法的第一部分完全按照你的意愿去做:

        if (n > 0) {
            System.out.println(n);
            test(n-1);
        // System.out.println(n); 
        }
        

        结果将是“54321” - 太棒了! 但是,由于测试方法末尾的第二个 println 方法,会发生什么,实际上是在堆叠输出(正如 Crazy Programmer 已经向我们展示的非常精确的那样)。换句话说:你根本不需要第二个 println 方法来达到你的目标!这就是递归的美妙之处:它在再次调用 test() 时“中断”,让你的第二个 println 方法“现在还没有执行”并重新开始整个过程​​。

        你可能看不到的是,通过使用

        if (n > 0)
        

        您正在验证 n 是否大于 0(因此至少为 1),而(同时)这也是您的中断条件!所以你的方法实际上要做的是:

        1. 检查,如果 n 大于 0,
        2. 打印 n 的值,然后(最后)
        3. 再次调用测试方法(使用 n-1)

        如果 n 达到 0,整个“方法堆叠”就会自行溶解(看看 Niels 的解决方案)并且不会返回任何进一步的 n 值。明白了吗?

        【讨论】:

          【解决方案5】:

          递归调用自身后,该方法仍然存在。处理完这些调用后,该方法将返回执行剩余的代码行。

          看看这个:http://ideone.com/zWqP8h

          public static void main(String[] args) {
                  test(5);
              }
              static void test(int n) {
                  if (n > 0) {
                  System.out.println("First step :" + n);
                  test(n-1);
                  System.out.println("Second step :" + n);
                  }
              }
          

          可能有助于澄清事情。

          【讨论】:

            【解决方案6】:

            我认为你应该跟踪代码然后你会得到解决方案。

            您的代码跟踪类似这样的内容。尝试调试此行,这可能会对您有所帮助。

            //for n = 5 
            void test(int n) {
                    if (n > 0) {
                    System.out.println(n);
                    test(n-1);----->// call of same function
                    //for n = 4
                    void test(int n) {
                        if (n > 0) {
                        System.out.println(n);
                        test(n-1);-----> 
                        //for n = 3
                        void test(int n) {
                            if (n > 0) {
                            System.out.println(n);
                            test(n-1);---->
                            //for n = 2
                            void test(int n) {
                                if (n > 0) {
                                System.out.println(n);
                                test(n-1);---->
                                //for n = 1
                                void test(int n) {
                                    if (n > 0) {
                                    System.out.println(n);
                                    test(n-1); ---->
                                    //for n = 0
                                    void test(int n) {
                                        if (n > 0) {//not satisfy
                                            System.out.println(n);
                                           test(n-1);
                                            System.out.println(n);
                                        } 
                                        // Till hear you were right but next things you missed.
            
                                    }//again resume of n = 1
                                    System.out.println(n);
                                    }
                                }//again resume of n = 2
                                System.out.println(n);
                                }
                            }//again resume of n = 3
                            System.out.println(n);
                            }
                        }//again resume of n = 4
                        System.out.println(n);
                        }
                    }//again resume of n = 5
                    System.out.println(n);
                    }
                }//Finish recursion.
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2015-10-09
              • 2016-02-22
              • 1970-01-01
              • 1970-01-01
              • 2016-10-14
              • 2013-09-29
              • 1970-01-01
              相关资源
              最近更新 更多