【发布时间】:2018-09-11 01:43:01
【问题描述】:
我试图解决一个问题,以获取给定整数的所有可能的有效括号组合。例如。 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 之后,我怎么会认为多次递归调用可能会更好?
我的问题不是针对上述问题,而是一般问题的类型。
【问题讨论】:
-
我认为关键提示是“分而治之”。 (en.wikipedia.org/wiki/Divide_and_conquer_algorithm ) 它通常意味着多次递归调用。
-
不确定我是否同意。我认为这更多地属于动态编程领域,而不是分而治之,尽管我可能错了。就像归并排序是分而治之——我可以将问题清楚地划分为 2 个子问题,最后合并结果。这似乎不是这里的情况。我不会像这样将输入投入到 2 中。
-
你确定吗?在我看来,您将问题分为两个问题:左侧问题和右侧问题。
-
不,我不完全确定。这就是我在这里发帖的原因。虽然我觉得不是。当我考虑归并排序或矩阵乘法时,我清楚地将输入问题分为 2、求解和组合。尽管在这里似乎并非如此,或者至少(对我而言)并不明显!
标签: java recursion dynamic-programming