我有一个List<List<T>>
两个列表尺寸的长度各不相同
Using recursion I can calculate all the combinations
一些示例List<List<T>>
及其组合
[
[1],
[2, 3],
[4, 5, 6]
]
// [1, 2, 4], [1, 2, 5], [1, 2, 6], [1, 3, 4], [1, 3, 5], [1, 3, 6]
[
[0, 4],
[3, 4, 1, 2]
]
// [0, 3], [0, 4], [0, 1], [0, 2], [4, 3], [4, 4], [4, 1], [4, 2]
[
["A", "B", "B"],
["C"]
]
// ["A", "C"], ["B", "C"], ["B, "C"]
组合的数量可以快速增长,使用递归会成为记忆和性能问题 如何计算迭代器来迭代组合?
做一些阅读我可能能够使用阶乘或组合数字系统,但我不确定如何在这种情况下应用它。
答案 0 :(得分:3)
您可以实现一个Iterator<List<T>>
,它只包含对原始元素的引用,作为列表列表和每个子列表的当前位置,并根据需要生成新的组合:
class Combinations<T> implements Iterator<List<T>> {
final List<List<T>> elements;
final int[] indices;
public Combinations(List<List<T>> elements) {
this.elements = elements;
this.indices = new int[elements.size()];
}
@Override
public boolean hasNext() {
// has first index not yet reached max position?
return indices[0] < elements.get(0).size();
}
@Override
public List<T> next() {
// get next
List<T> result = new ArrayList<>(indices.length);
for (int i = 0; i < indices.length; i++) {
result.add(elements.get(i).get(indices[i]));
}
// increase indices
for (int i = indices.length - 1; i >= 0; i--) {
indices[i]++;
if (indices[i] >= elements.get(i).size() && i > 0) {
indices[i] %= elements.get(i).size();
} else {
break;
}
}
return result;
}
}
增加指数的部分有点棘手。我猜这是因子编号系统发挥作用的部分,因为你基本上必须使用&#34; + 1&#34;一个数字(组合索引),其中每个数字具有不同的基数(各个子列表中的元素数量)。但是你可以通过一个简单的循环来完成它,而不使用任何特殊的库。
我为你的例子尝试了这个,它似乎有效:
List<List<Integer>> elements = Arrays.asList(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
Iterator<List<Integer>> combinations = new Combinations<>(elements);
combinations.forEachRemaining(System.out::println);
输出:
[1, 2, 4]
[1, 2, 5]
[1, 2, 6]
[1, 3, 4]
[1, 3, 5]
[1, 3, 6]
注意:这使用List<List<T>>
代替List<Set<T>>
,因为您需要索引嵌套集合,但您可以轻松地将其更改为接受List<Collection<T>>
并转换为List
在构造函数中。
答案 1 :(得分:1)
编辑此答案以适用于List<List<T>>
。 tobias_k的答案使用iterator
通过迭代列表的索引来获得下一个组合。这样做类似,没有基于迭代器的方法。我遵循的想法是:
* List 1: [1 2]
* List 2: [4 5]
* List 3: [6 7]
*
* Take each element from list 1 and put each element
* in a separate list.
* combinations -> [ [1] [2] ]
*
* Set up something called newCombinations that will contains a list
* of list of integers
* Consider [1], then [2]
*
* Now, take the next list [4 5] and iterate over integers
* [1]
* add 4 -> [1 4]
* add to newCombinations -> [ [1 4] ]
* add 5 -> [1 5]
* add to newCombinations -> [ [1 4] [1 5] ]
*
* [2]
* add 4 -> [2 4]
* add to newCombinations -> [ [1 4] [1 5] [2 4] ]
* add 5 -> [2 5]
* add to newCombinations -> [ [1 4] [1 5] [2 4] [2 5] ]
*
* point combinations to newCombinations
* combinations now looks like -> [ [1 4] [1 5] [2 4] [2 5] ]
* Now, take the next list [6 7] and iterate over integers
* ....
* 6 will go into each of the lists
* [ [1 4 6] [1 5 6] [2 4 6] [2 5 6] ]
* 7 will go into each of the lists
* [ [1 4 6] [1 5 6] [2 4 6] [2 5 6] [1 4 7] [1 5 7] [2 4 7] [2 5 7]]
现在的代码。我使用Set
只是为了摆脱任何重复。可以用List
替换。一切都应该无缝地工作。 :)
public static <T> Set<List<T>> getCombinations(List<List<T>> lists) {
Set<List<T>> combinations = new HashSet<List<T>>();
Set<List<T>> newCombinations;
int index = 0;
// extract each of the integers in the first list
// and add each to ints as a new list
for(T i: lists.get(0)) {
List<T> newList = new ArrayList<T>();
newList.add(i);
combinations.add(newList);
}
index++;
while(index < lists.size()) {
List<T> nextList = lists.get(index);
newCombinations = new HashSet<List<T>>();
for(List<T> first: combinations) {
for(T second: nextList) {
List<T> newList = new ArrayList<T>();
newList.addAll(first);
newList.add(second);
newCombinations.add(newList);
}
}
combinations = newCombinations;
index++;
}
return combinations;
}
一个小试块..
public static void main(String[] args) {
List<Integer> l1 = Arrays.asList(1,2,3);
List<Integer> l2 = Arrays.asList(4,5);
List<Integer> l3 = Arrays.asList(6,7);
List<List<Integer>> lists = new ArrayList<List<Integer>>();
lists.add(l1);
lists.add(l2);
lists.add(l3);
Set<List<Integer>> combs = getCombinations(lists);
for(List<Integer> list : combs) {
System.out.println(list.toString());
}
}