【问题标题】:Understanding the usecase of multiple recursive calls了解多次递归调用的用例
【发布时间】: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


【解决方案1】:

您可以将此问题视为 1 和 -1 的排列,当它们相加时,运行总和(从左到右相加数字时的临时值)不得小于 0,或者在以下情况下括号,不能使用比左括号更多的右括号。 所以:

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

因此,当您创建这些排列时,您会看到在每个递归调用中只能使用两个选项之一 - 1-1 - 并通过跟踪 @987654328 使用的内容@ 和 nRight 你可以让程序知道何时停止。

【讨论】:

  • 我认为您正在尝试解释上述解决方案的工作原理。那不是我要问的。我了解逻辑和停止条件等。我不明白的是如何直观地从解决方案1跳转到解决方案2。我怎么能想到“嗯,在这种情况下,多次递归调用我可能会更好”。这不是一个容易问的问题:)
  • 好吧,一旦你明白你只能追加两个之一——(),你会发现它是一个二叉树,你需要多次递归调用来探索所有叶子。如何从解决方案 1 跳转到解决方案 2 只是选择不同的方法。
  • 我无法将这个问题想象成二叉树。你能详细说明一下吗?
猜你喜欢
  • 2016-08-20
  • 1970-01-01
  • 2021-03-22
  • 1970-01-01
  • 2014-12-09
  • 1970-01-01
  • 2015-08-14
  • 2014-12-30
相关资源
最近更新 更多