你可以做的两件事:
1)坏事:
您可以继续当前的方法,但在添加对时,您需要:
a) 确保每一对总是按某种顺序排列,例如总是[smallerNumber, biggerNumber]
b) 保持 allPairs 列表排序
然后,当您完成计算时,删除重复项将是微不足道的。这是一个不好的方法,因为它会非常慢。
2)您可以使用不同的算法:
我将在这里做两个假设:
- 这是一个集合,所以我们没有重复
- 它们可以很容易地排序,即 1 到 5 我们知道它将是 1,2,3,4,5(但它也应该适用于案例 1,3,5,6)
基本上我们将有一个递归算法(就像你的一样)。输入将是“数字”-> 一个有序(升序)列表,输出将是一组对列表。让我们再次将此方法称为计算(列表编号)。
-> 当输入列表“数字”有 1 或 2 个元素时的基本情况。在这种情况下,返回一个包含一个列表的集合,该列表包含一个包含该 1 个元素或两者的“对”。即 numbers = [2,3] 或 numbers = [2] 然后您返回一个集合,其中包含一个列表,其中包含一个 (2,3) 或 (3) 的“对”。
-> 对于列表“数字”中包含第一个元素的每一对数字(即在数字 = [1,2,3,4,5] 的第一级递归中,它将是 [1,2], [1 ,3], [1,4], [1,5], [1,6]) 调用Set<List<Pair> result = compute(numbers.minus(currentPair))
。迭代该集合并在每个元素的开头添加当前对。
例子:
result = empty
numbers = [1,2,3,4,5]
first = (1,2), compute([3,4,5])
first = (3,4), compute([5])
return (5)
result = result + [(3,4)(5)]
first = (3,5) compute([4])
return (4)
result = result + [(3,5),(4)] (a this point result is [ [(3,4)(5)], [(3,5)(4)] ]
add (1,2) to each list in result which gives us [ [(1,2)(3,4)(5)], [(1,2)(3,5)(4)] ]
first = (1,3), compute([2,4,5])
first = (2,4), compute([5])
return (5)
result = result + [(2,4)(5)]
first = (2,5) compute([4])
return (4)
result = result + [(2,5),(4)] (a this point result is [ [(2,4)(5)], [(2,5)(4)] ]
add (1,3) to each list in result which gives us [ [(1,3)(2,4)(5)], [(1,3)(2,5)(4)] ]
at this point we have:
[ [(1,2)(3,4)(5)], [(1,2)(3,5)(4)], [(1,3)(2,4)(5)], [(1,3)(2,5)(4)] ]
对 (1,4)、(1,5) 和 (1,6) 继续此操作,您就完成了。您不必从 (2,3) 开始并执行 compute([1,4,5]) 因为这会导致重复。
该算法也应该适用于偶数集。
还没有测试过,没有证明但看起来不错,应该很容易编码,如果它有效,那么它应该很快,因为它只会进行必要的计算。
在代码中(我在这里编写了整个代码,因此它完全没有经过测试,可能包含编译和逻辑错误!):
public Set<List<Pair>> compute(List<Integer> numbers) {
if(numbers.size() < 3) {
// Base case
List<Pair> list = new ArrayList<>();
list.add(new Pair(numbers));
Set<List<Pair>> result = new HashSet<>();
result.add(list);
return result;
} else {
Set<List<Pair>> result = new HashSet<ArrayList<>>();
// We take each pair that contains the 1st element
for(int i = 1; i < numbers.size(); i++) {
Pair first = new Pair(numbers.get(0), numbers.get(i));
// This is the input for next level of recursion
// Our numbers list w/o the current pair
List<Integers> nextStep = new ArrayList<>(numbers);
nextStep.remove(i);
nextStep.remove(0);
Set<List<Pair>> intermediate = null;
if(nextStep.size() % 2 == 0) {
intermediate = compute(nextStep);
} else {
intermediate = compute(numbers).addAll( firstElementSingle(numbers) ),compute( nextStep );
}
for(List<Pair> list : intermediate ) {
// We add the current pair at the beginning
list.add(0, first);
}
result.addAll(intermediate);
}
return result;
}
}
如果我错过了什么,我自己很好奇,所以我期待您的反馈:-)
@编辑:
正如@yusuf 指出的那样,当输入是奇数列表时,这会丢失一些排列。它会错过 [1] 是单个元素的所有结果。
但是,如果我在这种情况下(奇数个元素)没有弄错,这应该可以工作:
compute(numbers).addAll( firstElementSingle(numbers) )
其中 firstElementSingle 是:
private Set<List<Integer>> firstElementSingle(List<Integer> numbers) {
Set<List<Integer>> result compute(numbers.subList(1,numbers.size()) );
for(List<Integer> list : result) {
list.add(numbers.get(0));
}
return result;
}
并且仍然只会产生必要的结果。