从递归方法中删除输入

时间:2017-08-14 12:46:53

标签: java arrays string algorithm recursion

早上好!我收到一个问题声明来编写一个方法,该方法返回传递的String输入的所有可能组合,例如

如果ABC通过,则返回[A,AB,BC,ABC,AC,B,C] 如果ABCD通过则返回[A,AB,BC,CD,ABC,AC,ACD,B,BCD,BD,ABD,AD,C,D,ABCD]

表示AB和BA始终相同,ABC,BAC和ACB也相同。

我最后写下了代码,但似乎工作(不确定)。

public static Set<String> getAnyPermutations(String s,String strInput) {
    Set<String> resultSet = new HashSet<>();
    char[] inp = strInput.toCharArray();
    for(int i=0; i<inp.length; i++) {
        String temp =s+String.valueOf(inp[i]);
        resultSet.add(temp);
        if(i+1<=inp.length)
            resultSet.addAll(getAnyPermutations(temp, String.valueOf(Arrays.copyOfRange(inp, i+1, inp.length))));
    }
    return resultSet; 
}

我的问题是,我想从方法中删除第一个参数(String s),因为它只用于内部计算,或者如果不可能,那么确保用户总是传递“”值或者我可以重置对于此方法的第一次(非递归)调用,它为“”。我很困惑如何在递归函数中做到这一点。 如果您怀疑除了这种情况可能会失败,请添加评论。

条件,所有必须仅在此函数内完成,不能创建其他方法。

4 个答案:

答案 0 :(得分:4)

  

只需在此功能内完成,不能创建其他功能。

然后你无法做到。该函数没有(合理) *知道它是自己调用还是被另一个函数调用的方式。

有许多涉及创建另一个功能的解决方案。一个可能符合你的要求,取决于他们实际表达的方式,就是让函数定义一个lambda来完成工作,并让lambda调用自己。例如,getAnyPermutations实际上不是递归的,它将包含递归函数。

但是这可能超出了界限,这取决于上面引用的确切含义,因为lambda是另一个函数,而不是可以从外部访问的函数。

* un 合理的方法是检查堆栈跟踪,您可以从Thread.currentThread().getStackTrace获取。

答案 1 :(得分:2)

您可以将当前方法更改为私有方法,并使用带有一个参数的公共方法将其连接,例如:

private static Set<String> getAnyPermutations(String s,String strInput) {
    Set<String> resultSet = new HashSet<>();
    char[] inp = strInput.toCharArray();
    for(int i=0; i<inp.length; i++){
        String temp =s+String.valueOf(inp[i]);
        resultSet.add(temp);
        if(i+1<=inp.length)
            resultSet.addAll(getAnyPermutations(temp, String.valueOf(Arrays.copyOfRange(inp, i+1, inp.length))));
    }
    return resultSet; 
}

现在,您可以向用户公开一个参数方法,然后用户将调用上述方法,例如:

public static Set<String> getAnyPermutations(String strInput) {
    return getAnyPermutations("", strInput);
}

<强>更新

如果您根本无法创建任何其他方法,那么唯一的选择就是使用var-args。但是,这需要更改实现,并且实际上并不限制用户传递多个值。

答案 2 :(得分:2)

您可以重写此特定算法,以便它不需要将状态传递到递归调用的调用。

(以Java为中心的伪代码):

Set<String> getAnyPermutations(String str) {
    if(str.length() == 0) {
        return Collections.emptySet();
    }

    String head = str.substring(0,1);
    String tail = str.substring(1);

    Set<String> permutationsOfTail = getAnyPermutations(tail);

    Set<String> result = new HashSet();

    // Head on its own
    // For input 'ABC', adds 'A'
    result.add(head);

    // All permutations that do not contain head
    // For input 'ABC', adds 'B', 'C', 'BC'
    result.addAll(permutationsOfTail);

    // All permutations that contain head along with some other elements
    // For input 'ABC', adds 'AB, 'AC', 'ABC'
    for(String tailPerm : permutationsOfTail) {
       result.add(head + tailPerm);
    }

    return result;
}

这符合您不创建任何额外方法的目的 - 但请注意,如果将for循环提取到允许Set<String> prefixEachMember(String prefix, Set<String> strings)的新方法result.addAll(prefixEachMember(head,permutationsOfTail))中,则代码会更干净。< / p>

然而并非总能做到这一点,有时你想要携带状态。一种方式是你要求避免的方式,但我会将其包括在我的答案中,因为它是实现目标的一种清晰而通用的方式。

 public Foo myMethod(Bar input) {
      return myMethod(new HashSet<Baz>(), input);
 }

 private Foo myMethod(Set<Baz> state, Bar input) {
      if(...) {
          return ...;
      } else {
          ...
          return myMethod(..., ...); 
      }
 }

这里,第一种方法是您的公共API,其中不需要collector / state参数。第二种方法是私有worker方法,最初使用空状态对象调用它。

另一个选择是引用一个对象字段。但是,我建议不要这样做,因为当递归代码引用全局对象时,它会变得混乱。

答案 3 :(得分:2)

您始终可以将递归方法转换为其迭代等效方法 - 例如 Way to go from recursion to iteration

在迭代版本中,很容易不公开状态参数(现在只需要在迭代方法的开头初始化它)。

这一般来说不太实用(但我相信这个问题的目的更具理论性,否则它只是暴露另一种方法的好方法)。

此外,在这种特殊情况下,您可能会考虑这种简单的迭代方法(虽然它不是通过直接翻译给定代码获得的):

public static Set<String> getAnyPermutations(String strInput) {
    Set<String> resultSet = new HashSet<>();
    char[] inp = strInput.toCharArray();

    for (int bitMask = 0; bitMask < (1 << inp.length); bitMask++) {
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < inp.length; i++) {
            if ((bitMask & (1 << i)) != 0) {
                str.append(inp[i]);
            }
        }

        if (str.length() > 0) {
            resultSet.add(str.toString());
        }
    }

    return resultSet;
}