【问题标题】:Java recursion with two variables带有两个变量的Java递归
【发布时间】:2015-08-04 03:04:17
【问题描述】:

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

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;

这显然既不正确也不完整。请帮助我了解堆栈是如何建立的。

【问题讨论】:

  • 您是在询问实际的调用约定以及堆栈帧是如何制作的,还是递归树的垂直高度?
  • 实际调用约定
  • 您应该通过调试器运行它,这样您就可以看到每次调用该方法的输出,以及您调用该方法的次数。

标签: java recursion


【解决方案1】:

理论

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

一种思维方式就像一棵树:

我只认为栈按照x/2增长

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

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

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

【讨论】:

  • 这张图让事情变得清晰起来。我实际上是递归的新手,我不是在谈论算法的运行时间。谢谢
  • 那个递归将是讨厌!当然,你会注意到x-2在这里占主导地位,而不是x/2,所以它可能不是对数的。
  • 啊,Master theorem,我记得...
【解决方案2】:

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

每次我们从最初的q1(x/2) 调用之一返回时,我们都必须遵循用于计算bq1(x-2) 调用。该递归调用还将对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) 进行 7 次递归调用(64/2、32/2、...、1/2 = 0)。但它会对q1(x-2) (64-2, 62-2, 60-2, ..., 2-2 = 0) 进行更多的递归调用。

在他的画中,如果右子树更大一些会更真实,因为该子树需要更长的时间才能触底。事实上,你可以在我的图表中看到这一点。如果将垂直和对角箭头视为树的分支,则使用 x/2 的第一个递归调用的子树只有 5 个节点,而使用 x-2 的第一个递归调用的子树有 7 个节点。几乎总是如此。

【讨论】:

    【解决方案3】:

    要知道实际调用转换从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。调试将帮助您更好地理解。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-21
      • 1970-01-01
      • 2013-09-16
      • 2021-07-15
      • 2021-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多