我试图解决一个问题,以获得给定整数的所有可能的有效括号组合。例如。 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之后,我怎么能认为通过多次递归调用我可能会更好?
我的问题不是针对上述问题,而是一般问题的类型。
答案 0 :(得分:0)
你可以将这个问题视为1和-1的排列,它们在一起求和时,运行总和(从左到右加上数字时的临时值)不得小于0,或者如果是括号,不得使用比左括号更多的右括号。 所以:
如果n=1
,那么您只能1+(-1)
。
如果n=2
,则可以1+(-1)+1+(-1)
或1+1+(-1)+(-1)
。
因此,当您创建这些排列时,您会发现在每次递归调用中,您只能使用两个选项中的一个 - 1
或-1
,并跟踪已经存在的内容与nLeft
和nRight
一起使用,您可以让程序知道何时停止。