从递归方法中删除输入
Posted
技术标签:
【中文标题】从递归方法中删除输入【英文标题】:Removing an input from a recursive method 【发布时间】:2017-08-14 12:46:53 【问题描述】:早上好!我收到了一个问题陈述来编写一个方法,该方法返回传递的字符串输入的所有可能组合,例如
如果通过了 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),因为它只用于内部计算,或者如果这不可能,那么确保用户总是传递一个“”值或者我可以重置对于此方法的第一次(非递归)调用,它为“”。我很困惑如何在递归函数中做到这一点。 如果您怀疑它在这种情况之外可能会失败,也请添加评论。
条件,只能在这个函数内部完成,不能创建其他方法。
【问题讨论】:
你可能想使用方法重载。 @MikeCAT:那将创建另一个“功能”。 “不能创建其他函数” - 为什么不呢?提取方法通常是一个非常好的重构。 【参考方案1】:只能在这个函数内部完成,不能创建其他函数。
那你就做不到了。该函数没有(合理的)* 方法来知道它是调用自己还是被另一个函数调用。
有很多解决方案涉及创建另一个函数。 可能满足您的要求的一种方法,取决于它们的实际表达方式,是让函数定义一个 lambda 来完成工作,并让 lambda 自己调用。例如,getAnyPermutations
实际上不会是递归的,它会包含一个递归函数。
但这可能超出了上面引用的确切含义,因为 lambda 是另一个函数,只是不能从外部访问。
* un合理的方法是检查堆栈跟踪,您可以从Thread.currentThread().getStackTrace
获得。
【讨论】:
【参考方案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;
【讨论】:
这太棒了,效果惊人..但我不明白有人怎么能想到这个解决方案..你是如何解决问题和这些位移的,你是这些方面的专家还是有什么办法朝那个方向思考?非常感谢! 这是一种已知的技术,用于迭代生成一组大小为 N 的所有子集。例如,请参阅此链接:***.com/questions/728972/…【参考方案3】:您可以将当前方法更改为私有方法,并将其与具有一个参数的公共方法接口,例如:
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
。但是,这需要更改实现,实际上并不限制用户传递多个值。
【讨论】:
【参考方案4】:您可以重写此特定算法,使其不需要将状态传递到递归调用的调用。
(以 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
循环提取到允许result.addAll(prefixEachMember(head,permutationsOfTail))
的新方法Set<String> prefixEachMember(String prefix, Set<String> strings)
中,代码会更简洁。
然而并不总是可以做到这一点,有时你确实想要携带状态。一种方式是您要求避免的方式,但我将把它包含在我的回答中,因为它是实现目标的一种干净且常见的方式。
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,其中不需要收集器/状态参数。第二种方法是私有工作方法,最初调用时使用一个空状态对象。
另一个选项是引用一个对象字段。但是,我建议不要这样做,因为当递归代码引用全局对象时,它会变得混乱。
【讨论】:
如果return Collections.emptySet();
不是return Collections.singleton("")
,您可以省略result.add(head);
,因为它成为循环的一部分。就其本身而言,我认为包含空字符串的 1 元素集是空字符串输入情况的正确解决方案。
@RalfKleberhoff 好吧,我想 OP 可以决定他们的要求。我会说空字符串的正确输出是一个空集。
正确。我没有仔细阅读OP声明。 OP给我们的参考结果中没有空字符串。这排除了我对您的答案的修改..以上是关于从递归方法中删除输入的主要内容,如果未能解决你的问题,请参考以下文章