带有两个变量的Java递归

时间:2015-08-04 03:04:17

标签: java recursion

我一直在试图弄清楚这种递归方法的堆栈是什么样的。

public class Apples {

    public static void main (String [] args) {
       q1(5);   
    }

    public static int q1 (int x) {
        if (x < 1) {
            System.out.println(x);
            return 1;
        }
        int a = 3 + q1(x / 2);
        int b = 2 * q1(x - 2) + 1;
        System.out.println(x + ";" + a + ";" + b);
        return a + b;
    }
}

但到目前为止,我只认为堆栈按照x / 2增长:

x=0   returns 1;
x=1  a=4  b=3     returns 7;
x=2  a=10 b=3     returns 13;
x=5  a=16 b=9     returns 19;

这显然既不真实也不完整。请帮我理解堆栈是如何构建的。

3 个答案:

答案 0 :(得分:11)

<强>理论

每次,此函数将首先递归q1(x/2)路径,直到达到结束条件。然后,将处理所有待处理的q1(x-2)调用。现在,这是我们第一次处理q1(x-2)的每个q1(x/2)时变得棘手的地方。因此,我们现在回到与以前相同的位置,只有一层向下,重复直到我们处理所有q1(x-2)次调用(在最后q1(x/2)层中)。

想到它的一种方式就像一棵树:

3 layers of the recursion tree. (c) The Brofessor

  

我只认为堆栈根据x / 2

增长

你是对的,如果你的意思是这个函数在q1(x/2)中比在q1(x-2)方向上更快地递归。尽管如此,你所说的意味着它以 lg(n)的方式增长( lg(n)是基础2)。

但是,我们仍然需要分析其他堆栈帧,因此我们设置以下递归关系:

T(n)= T(n / 2)+ T(n-2)+ c1

答案 1 :(得分:4)

由于为计算a的值而进行的重复递归调用,此递归函数的堆栈最初会增长;也就是说,我们将继续调用q1(x/2)直到x/2 < 1,在这种情况下,我们已经达到了递归的基本情况,并且可以简单地返回1.

每次我们从最初的q1(x/2)来电之一返回时,我们都必须按照q1(x-2)调用来计算b。该递归调用还将对a进行一系列连续的递归调用(因为a首先在函数中计算),这遵循相同的规则;在每个返回之后,我们对b进行递归调用,并且这个过程重复进行,直到我们到达所有调用分支的基本情况。

这里是堆栈的外观。读取它的顺序是首先按照垂直箭头,返回,然后按照对角线箭头。按照斜箭头后重复此过程。如果没有箭头,请返回。

顺便说一下,返回函数的堆栈帧将被完全取消分配,并且一个新的函数调用(如果有的话)将取代它。您可以看到,在任何给定时间,活动的堆栈帧数不超过4个。当最后一个最顶层的堆栈帧完成时,它被解除分配,并且它的位置由下面和右边的堆栈获取。你从那里回来,依此类推......

希望这个图有助于清理它。

                |                  |                  |
                |                  |                  |
                |                  |                  |
  +--------+    |                  |                  |
  | a =    |    |                  |                  |
  | b =    |    |                  |                  |
  +--------+    |                  |                  |
  | x = 0  |
  +--------+
  returns 1 

       ^             +--------+
       |             | a =    |
       |             | b =    |
       |             +--------+
       |          /  | x = -1 |
       |         /   +--------+
       |        /    returns 1
       |       /
  +--------+  /            
  | a = 4  | /             
  | b = 3  |/              
  +--------|               
  | x = 1  |               
  +--------+               
  returns 7                

       ^             +--------+
       |             | a =    |
       |             | b =    |
       |             +--------+
       |          /  | x = 0  |
       |         /   +--------+
       |        /    returns 1
       |       /
  +--------+  /           
  | a = 10 | /             
  | b = 3  |/              
  +--------+               
  | x = 2  |               
  +--------+               
  returns 13               

       ^             +--------+
       |             | a =    |
       |             | b =    |
       |             +--------+
       |             | x = 0  |
       |             +--------+
       |             returns 1
       |
       |                  ^             +--------+
       |                  |             | a =    |
       |                  |             | b =    |
       |                  |             +--------+
       |                  |          /  | x = -1 |
       |                  |         /   +--------+
       |                  |        /    returns 1
       |                  |       /
       |             +--------+  /
       |             | a = 4  | /
       |             | b = 3  |/
       |             +--------+
       |             | x = 1  |
       |             +--------+
       |             returns 7
       |                  
       |                  ^             +--------+
       |                  |             | a =    |
       |                  |             | b =    |
       |                  |             +--------+
       |                  |             | x = 0  |
       |                  |             +--------+
       |                  |             returns 1
       |                  |
       |                  |                  ^             +--------+
       |                  |                  |             | a =    |
       |                  |                  |             | b =    |
       |                  |                  |             +--------+
       |                  |                  |          /  | x = -1 |
       |                  |                  |         /   +--------+
       |                  |                  |        /    returns 1
       |                  |                  |       /
       |                  |             +--------+  /
       |                  |             | a = 4  | /
       |                  |             | b = 3  |/
       |                  |             +--------+
       |                  |          /  | x = 1  |
       |                  |         /   +--------+
       |                  |        /    returns 7               
       |                  |       /                    
       |                  |      /                     
       |             +--------+ /                      
       |             | a = 10 |/                       
       |             | b = 15 |                        
       |             +--------+                        
       |          /  | x = 3  |                        
       |         /   +--------+                        
       |        /    returns 25                        
       |       /                                       
  +--------+  /                                        
  | a = 16 | /                                         
  | b = 51 |/                                          
  +--------+    |                  |                  |
  | x = 5  |    |                  |                  |
  +--------+    |                  |                  |
  returns 67    |                  |                  |
                |                  |                  |
                |                  |                  |
                |                  |                  |

Brofessor有一个很好的理论方法,但他说的有点不准确;当他说q1(x/2)的速度比q1(x-2)更快时,他意味着前者与后者相比很快会达到基本情况。考虑大于5的数字。对于x的大值,x/2远小于x-2。因此,x-2情况最终会产生比x/2情况更多的递归调用,因此x-2调用支配堆栈的增长。

例如,q1(64)将对q1(x/2)(64 / 2,32 / 2,...,1/2 = 0)进行7次递归调用。但它会有更多的递归调用q1(x-2)(64-2,62-2,60-2,...,2-2 = 0)。

在他的绘图中,如果正确的子树更大,那么更现实的是,因为该子树将需要更长时间才能达到最低点。实际上,您可以在我的图表中看到这一点。如果您将垂直和对角线箭头视为树的分支,则使用x/2的第一个递归调用的子树只有5个节点,而使用x-2的第一个递归调用的子树有7个节点节点。几乎总是如此。

答案 2 :(得分:2)

要知道实际的通话转换以q1(0), q1(1)...

开头

我可以帮助您q1(2),然后您可以轻松尝试q1(5)

x = -1, q1(-1) => 1 // "q1(-1) => 1" means q1 returns 1
x = 0, q1(0) => 1 // "q1(0) => 1" means q1 returns 1

x = 1, a = 3 + q1(0) = 3 + 1 = 4
       b = 2 * q1(-1) + 1 = 2*1 + 1 = 3
       q1(1) => 7

x = 2, a = 3 + q1(1) = 3 + 7 = 10
       b = 2 * q1(0) + 1 = 2*1 + 1 = 3
       q1(2) => 13

...

因此,您可以打印q1(2),然后输出13。调试将帮助您更好地理解。