Java递归:示例

时间:2014-05-29 11:42:32

标签: java recursion

我知道递归是如何工作的,即:

method calls itself until it reaches a base case then it can start solving its problem.

在这个代码示例中是一种方法或从花瓶中移除花朵。

我添加了一个跟踪声明,可以看到每次通话后花瓶里有多少朵花。然而,输出在花瓶中留下7朵花。我很困惑为什么?

代码:

public static void emptyVase( int flowersInVase ) {
    if( flowersInVase > 0 ) {
    // take one flower and
        emptyVase( flowersInVase - 1 ) ;

        System.out.println(flowersInVase);


    } else {
           // the vase is empty, nothing to do

    }
}

调用方法:

public class RecursionPractice {

    public static void main(String[] args) {

        emptyVase(7);    
    }

输出:

1
2
3
4
5
6
7

5 个答案:

答案 0 :(得分:8)

在递归中,调用的顺序非常重要!当您展开时,您可以更好地理解您自己的示例。它看起来像这样:

// step 1
// flowerInVase is greater than 7, so the first thing to do is call the function again! 
// Note that the System.out.println is NOT yet reached because of the execution of the function!
// call emptyVse(7 - 1), the *stack* has *remembered* to the current value of *floweInVase* => 7
emptyVase(7); 
// step 2
emptyVase(6);
// condition is *true* yet again, so the same rules as above apply
// current *remembered* value of `floweInVase` is 6
// step 3
emptyVase(5);
// and so on ... until `flowerInVase` is 0 ... now ...

现在堆栈看起来像这样:

emptyVase(7)
    emptyVase(6)
        emptyVase(5)
            emptyVase(4)
                emptyVase(3)
                    emptyVase(2)
                        emptyVase(1)
                            emptyVase(0) 
                                -> nothing to do, recursion is stopped.
                                -> so go back to previous 
                                -> *stack frame* which is 1
                        System.out.println(1);
                    System.out.println(2);
                System.out.println(3);
            System.out.println(4);
        System.out.println(5);
    System.out.println(6);
System.out.println(7);

堆栈框架中,emptyVase(1)函数执行已完成,因此请打印 1 的当前flowerInVase。每次打印当前存储的变量时,返回上一个堆栈帧,直到到达最后一个堆栈帧

这就是订单逆转的原因!这也是为什么如果你改变打印顺序和函数执行它会看起来像预期的那样。

public static void emptyVase( int flowersInVase ) {
    // if there is a flower to take from the vase
    if( flowersInVase > 0 ) {
        // print the count of flowers BEFORE one is taken!
        System.out.println(flowersInVase);
        // take one flower and put it aside
        emptyVase( flowersInVase - 1 ) ;
    } else {
           // the vase is empty, nothing to do
           System.out.println(flowersInVase);
           // print 0!
    }
}

这将产生:

7
6
5
4
3
2
1
0

花瓶实际上是空的,但因为你的条件是flowerInVase > 0,这意味着最后一次调用是用emptyVase(0) < em> else 部分被采取,你不会在那里打印计数器的值。在那里添加打印件,您将看到空花瓶

理解递归的方法(和示例)很好。我认为在你的例子中要注意的重要事实是,递归调用中断当前函数调用并启动一个新函数(再次执行相同的函数),但前一个调用是记住,新呼叫完成后,继续从中断的地方开始!

您可以将每个递归调用想象为在框中创建

|-------------------------------|
|--- emptyVase(7)           --- |
|                               |
|   |--- emptyVase(6)       ---||
|   |                          ||
|   |   |--- emptyVase(5)   ---||
|   |   |                      ||
|   |   |   |... and so on     ||
|   |   |   |---emptyVase(0)---||
|   |   |   | S.out.println(0) ||
|   |   |   |------------------||
|   |   |----------------------||
|   |   System.out.println(6)  ||
|   |--------------------------||
|   System.out.println(7);     ||
|-------------------------------|

更深递归,你拥有的盒子就越多。

这也是问题所在。递归在内存分配方面非常昂贵,并且因为计算机具有有限的内存量,如果递归创建太多框,则程序的执行达到允许的最大盒数=堆栈帧并说堆栈溢出。请注意,我的解释是非常基本的。要详细解释所谓的调用堆栈,请查看此Wikipedia article - Call stack

答案 1 :(得分:1)

每次拨打emptyVase()都会打印flowersInVase值,所以基本上您会拨打System.out.println() 7次。第一个打印'1'用于最后一次调用emptyVase(),第二个'2'用于前一个调用,所以再次调用。最后一个'7'用于第一次调用{{1}女巫真是花瓶里的7朵花。

答案 2 :(得分:0)

Nopes。方法很好。它会一直从花瓶中取出一朵花,直到花瓶空了。如果您期望以下输出(假设使用flowersInVase调用该方法为10):

10 9 8 7 6 5 4 3 2 1

然后你应该写System.out.println(flowersInVase); emptyVase( flowersInVase - 1 );而不是emptyVase( flowersInVase - 1 ); System.out.println(flowersInVase);

答案 3 :(得分:0)

尝试使用int []代替(只复制并粘贴代码并将数组替换为int - 您必须验证它是否有效)

public static void emptyVase( int[] flowersInVase ) {
      if( flowersInVase[0] > 0 ) {
       // take one flower and
        emptyVase( flowersInVase[0]-- ) ;

        System.out.println(flowersInVase[0]);

      } 
    }

...

public class RecursionPractice {

public static void main(String[] args) {

    emptyVase(new int[]{7});

}

它不能使用原始int的原因是因为只传递了int的,而不是对保存该值的内存位置的引用,是您需要拥有的,以便在所有方法调用中反映出更改。

答案 4 :(得分:-1)

Print the flowersInVase before the recursive call. That will solve your confusion like below.

public static void emptyVase( int flowersInVase ) {
          if( flowersInVase > 0 ) {
           // take one flower and


            System.out.println(flowersInVase);  // **** Moved the print Here **********


            emptyVase( flowersInVase - 1 ) ;




          } else {
           // the vase is empty, nothing to do
              System.out.println("Hurray It's empty now..");
          }
        }



public class RecursionPractice {

    public static void main(String[] args) {

        emptyVase(7);
    }