【发布时间】:2019-03-11 10:34:32
【问题描述】:
给定一个对象数组,当数组中的值可能重复时,我需要尽可能高效地找到给定数组的所有不同子集集,包括所有值。
例如:如果数组是1, 2, 1, 2,那么我需要创建以下多重集:
{[1], [1], [2], [2]}{[1], [1], [2, 2]}{[1], [2], [1, 2]}{[1], [1, 2, 2]}{[1, 1], [2], [2]}{[1, 1], [2, 2]}{[1, 2], [1, 2]}{[1, 1, 2], [2]}{[1, 1, 2, 2]}
请注意,子集中值的顺序和多重集中子集的顺序都不重要。像{[1, 2, 2], [1]} 这样的多集与#4 相同,而{[2, 1], [2], [1]} 与#3 相同。
这里的例子是整数,但实际上我必须用对象来做。
这应该尽可能高效。最好只计算正确的(不重复的)多重集,而不检查是否已经出现,因为创建它的方式将消除这种情况。
我知道如何使用二进制表示创建所有子集。我用它结合递归来计算所有多重集。这完美地工作,除了当值重复时它不起作用。这是我到目前为止所做的:
(a 是给定数字的数组, curr 是当前正在构建的多重集, b 是所有多重集的最终集。)
public static void makeAll(ArrayList<Integer> a,
ArrayList<ArrayList<Integer>> curr,
ArrayList<ArrayList<ArrayList<Integer>>> b) {
ArrayList<ArrayList<Integer>> currCopy;
ArrayList<Integer> thisGroup, restGroup;
int currSize = 0, ii = 0;
if (a.size() == 0)
b.add(new ArrayList<ArrayList<Integer>>(curr));
else {
for (int i = 0; i < 1 << (a.size() - 1); i++) {
thisGroup = new ArrayList<>();
restGroup = new ArrayList<>();
ii = (i << 1) + 1; // the first one is always in, keeps uniquness.
for (int j = 0; j < a.size(); j++)
if ((ii & 1 << j) > 0)
thisGroup.add(a.get(j));
else
restGroup.add(a.get(j));
currSize = curr.size();
curr.add(new ArrayList<Integer>(thisGroup));
makeAll(restGroup, curr, b);
curr.subList(currSize, curr.size()).clear();
}
}
}
提前致谢!
【问题讨论】:
-
不知道你的算法能不能改进,我现在在手机上。但是应该使您的算法与重复元素一起工作的一个想法是使用索引而不是元素。对于您的示例,使用
[0,1,2,3],从该集合中创建所有子集,然后简单地将每个索引替换为其原始列表中的相应元素。 -
也许有一些聪明的方法可以优化为范围创建多组子集。