【问题标题】:Need help understanding this double recursion需要帮助理解这种双重递归
【发布时间】:2016-11-15 07:17:47
【问题描述】:

我有一些代码

public class NameGenerator {
public static void main (String[] args)
{
    List<String> result = new ArrayList<String>();
    buildNameChoices("von.del.smith", result);
    System.out.println(result);
}
public static void buildNameChoicesHelper(String[] nameArray, int nameIndex,
    String firstName, String lastName, List<String> result) {

    if(nameIndex >= nameArray.length) {
        if(lastName.length() > 0) {
            result.add(firstName + lastName);
        }
    }
    else {
        System.out.println("Calling first buildNameChoices");
        buildNameChoicesHelper(nameArray, nameIndex + 1,firstName, lastName, result);
        System.out.println("Calling second buildNameChoices");
        buildNameChoicesHelper(nameArray, nameIndex + 1,firstName, lastName + "." + nameArray[nameIndex], result);
    }   
}
public static void buildNameChoices(String nameStr, List<String> result) {
    String[] nameArray = nameStr.split("\\.", -1);
    for(int i = 0; i < nameArray.length; i++) {
        System.out.println("Inside for loop");
        buildNameChoicesHelper(nameArray, i + 1, nameArray[i], "", result);
    }
}

}

生成它所传递的名称字符串的所有可能组合。代码有效,我理解递归在某种程度上是如何工作的,但是双重递归调用真的让我感到困惑。我已经看了很长时间了,但我很难准确地理解它在做什么。对此的任何帮助将不胜感激。试了调试,还是看不懂。

【问题讨论】:

  • 你不明白的到底是什么?双递归的概念?或者为什么这种特殊情况有效?
  • 几乎是整个概念。我真的不明白什么时候调用第二个。是直接在第一个之后吗?还是第一个调用基本情况,然后第二个调用?
  • 试试看this,这是对双递归的一个很好的解释。
  • 谢谢,我确实看过那个问题,但我仍在为这个概念而苦苦挣扎。也许如果我继续看它,它会开始点击。
  • 如果您从堆栈帧(运行方法的实例)的角度来考虑,每当进行递归调用时,堆栈顶部都会放置另一个帧。在双递归的情况下,一次仍然只有一个堆栈,但堆栈会在原始方法结束之前增长和收缩多次。这是因为当在原始方法中进行第一次递归调用时,它必须在执行该方法中的第二次递归调用之前解决该调用。在原始方法调用的方法中进行递归调用也是如此。

标签: java recursion


【解决方案1】:

您可以将递归想象成一个执行堆栈。

  1. buildNameChoicesHelper(nameArray, nameIndex + 1,firstName, lastName, result); 被调用并添加到栈顶

  2. 如果 if(nameIndex &gt;= nameArray.length) 匹配,我们查看堆栈中是否还有元素,如果有,我们向下一个并进入第 3 步,否则我们完成。如果条件不匹配,则执行步骤 1

  3. 我们从buildNameChoicesHelper(nameArray, nameIndex + 1,firstName, lastName, result);的调用返回,因此继续调用添加到堆栈顶部的buildNameChoicesHelper(nameArray, nameIndex + 1,firstName, lastName + "." + nameArray[nameIndex], result);,从1开始

您的示例的调用堆栈如下所示:

//execution 1
buildNameChoicesHelper(nameArray: [von, del, smith],nameIndex: "2", firstName: "von", lastName: "", result: "[]");

stack = [execution1];

//execution 2
buildNameChoicesHelper(nameArray: [von, del, smith],nameIndex: "3", firstName: "von", lastName: "", result: "[]");

stack = [execution1, execution2];

// nameIndex >= nameArray.length -> step 2 -> step3
stack = [execution1];   

// execution 3
buildNameChoicesHelper(nameArray: [von, del, smith],nameIndex: "3", firstName: "von", lastName + "." + nameArray[nameIndex]: ".smith", result: "[]");

stack = [execution1, execution3];

// nameIndex >= nameArray.length -> step 2 -> step3
stack = [execution1];   

//execution 4
buildNameChoicesHelper(nameArray: [von, del, smith],nameIndex: "2", firstName: "von", lastName + "." + nameArray[nameIndex]: ".del", result: "[von.smith]");

stack = [execution1,execution4];   


// exectution 5
// we are back at step 1
buildNameChoicesHelper(nameArray: [von, del, smith],nameIndex: "3", firstName: "von", lastName: ".del", result: "[von.smith]");

stack = [execution1,execution4,execution5];   

// nameIndex >= nameArray.length -> step 2 -> step3    
stack = [execution1,execution4];   

// execution 6
buildNameChoicesHelper(nameArray: [von, del, smith],nameIndex: "3", firstName: "von", lastName + "." + nameArray[nameIndex]: ".del.smith", result: "[von.smith, von.del]");

stack = [execution1,execution4,execution6];  

// nameIndex >= nameArray.length -> step 2 -> step3    
stack = [execution1,execution4];   

// execution 7
buildNameChoicesHelper(nameArray: [von, del, smith],nameIndex: "3", firstName: "del", lastName: "", result: "[von.smith, von.del, von.del.smith]");

stack = [execution1,execution4,execution7];  

// nameIndex >= nameArray.length -> step 2 -> step3 
stack = [execution1];  

// execution 8
buildNameChoicesHelper(nameArray: [von, del, smith],nameIndex: "3", firstName: "del", lastName + "." + nameArray[nameIndex]: ".smith", result: "[von.smith, von.del, von.del.smith]");

stack = [execution1, execution8];  

//1 and 8 both terminate

【讨论】:

  • 这实际上很有帮助。我认为当您有多个等待执行的堆栈帧时很容易感到困惑。
  • 一个快速的问题。我看到在执行 3 中调用第二个递归方法时,索引仍然是 3,就像在第一个中一样。为什么不是 4?
  • execution1调用execution 3,索引还是2,所以是2+1=3
  • 我想我现在和你在一起。当调用第二个递归方法时,我们仍在第一次递归调用。
  • 是的。第一个递归被调用,直到最后一个调用完成,然后最后一个的调用者调用它的第二个递归,这基本上是相同的。
猜你喜欢
  • 1970-01-01
  • 2020-07-04
  • 1970-01-01
  • 2020-05-03
  • 2023-03-23
  • 2021-11-29
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
相关资源
最近更新 更多