理解多个递归调用的用法

时间:2018-04-01 18:11:25

标签: java recursion dynamic-programming

我试图解决一个问题,以获得给定整数的所有可能的有效括号组合。例如。 input: n = 2, output: (()), ()()

显然当n增加时,当前输出建立在前一个n的输出上。因此,通过获取先前的结果并添加到它以获得当前结果,很容易得出递归:

HashSet<String> getCombinations(int n)
{
    HashSet<String> result = new HashSet<String>();
    if (n <= 0) return null;
    if (n == 1)
    {
        result.add("()");
        return result;
    }
    for (String str: getCombinations(n - 1))
    {
        for (int i = 0; i < str.length(); i++)
        {
            result.add(new StringBuffer(str).insert(i, "()").toString());
        }
    }
    return result;
}

虽然上面代码的缺点显然是重复生成但未存储的相同结果值。所以我在网上寻找更好的解决方案(因为我无法想到),并找到了这个:

ArrayList<String> result = new ArrayList<>();
void getCombinations(int index, int nLeft, int nRight, String str)
{
    if (nLeft < 0 || nRight < 0 || nLeft > nRight) return;
    if (nLeft == 0 && nRight == 0)
    {
        result.add(str);
        return;
    }
    getCombinations(index + 1, nLeft - 1, nRight, new StringBuffer(str).insert(index, "(").toString());
    getCombinations(index + 1, nLeft, nRight - 1, new StringBuffer(str).insert(index, ")").toString());
}

我理解这个解决方案是如何工作的,为什么它比第一个更好。但即使是现在,我无法想象看第一个解决方案,然后提出第二个解决方案。我如何直观了解何时使用多个递归调用?换句话说,在实现解决方案1之后,我怎么能认为通过多次递归调用我可能会更好?

我的问题不是针对上述问题,而是一般问题的类型。

1 个答案:

答案 0 :(得分:0)

你可以将这个问题视为1和-1的排列,它们在一起求和时,运行总和(从左到右加上数字时的临时值)不得小于0,或者如果是括号,不得使用比左括号更多的右括号。 所以:

如果n=1,那么您只能1+(-1)。 如果n=2,则可以1+(-1)+1+(-1)1+1+(-1)+(-1)

因此,当您创建这些排列时,您会发现在每次递归调用中,您只能使用两个选项中的一个 - 1-1,并跟踪已经存在的内容与nLeftnRight一起使用,您可以让程序知道何时停止。