以下代码查找一组数字的所有子集的复杂性是什么?
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> subsets = new ArrayList<>();
subsets.add(new ArrayList<Integer>());
return find(subsets, nums, 0);
}
private List<List<Integer>> find(List<List<Integer>> subsets, int[] nums, int index) {
if (index == nums.length) {
return subsets;
}
List<List<Integer>> newSubsets = new ArrayList<>();
for (List<Integer> subset: subsets) {
List<Integer> newSubset = new ArrayList<>();
newSubset.addAll(subset);
newSubset.add(nums[index]);
newSubsets.add(newSubset);
}
subsets.addAll(newSubsets);
return find(subsets, nums, index + 1);
}
它是O(2^nums.length)
,因为那是子集的数量,你必须将每个子集添加到返回的列表中吗?另外,我认为下面的版本渐近仍然是O(2^set.size())
,但是一般递归所贡献的空间复杂度是O(set.size())
,而在上面的尾递归代码中,它是O(1)
?
public static ArrayList<ArrayList<Integer>> getSubsets(ArrayList<Integer> set) {
ArrayList<ArrayList<Integer>> subsets = new ArrayList<>();
getSubsets(set, subsets, 0);
return subsets;
}
private static void getSubsets(ArrayList<Integer> set, ArrayList<ArrayList<Integer>> subsets, int index) {
if (index == set.size()) {
subsets.add(new ArrayList<Integer>());
return;
}
getSubsets(set, subsets, index + 1);
int item = set.get(index);
ArrayList<ArrayList<Integer>> moreSubsets = new ArrayList<>();
for (ArrayList<Integer> subset: subsets) {
ArrayList<Integer> newSubset = new ArrayList<>();
newSubset.addAll(subset);
newSubset.add(item);
moreSubsets.add(newSubset);
}
subsets.addAll(moreSubsets);
}
答案 0 :(得分:0)
在我看来,这里的主要问题是数据空间的复杂性。如果原始数组的长度超过几十个,您的代码将抛出OutOfMemoryError。地球上的更多元素和所有内存芯片和硬盘驱动器都不足以存储子集。
通过优化尾部递归来节省几个字节并不会产生影响。
出于实际目的,以下代码非常有效。虽然它通常较慢,但它的数据空间复杂度为O(N)并且它会动态计算子集,因此如果您只想处理某些子集,它甚至可以为您提供更好的结果
private static List<Integer> getSubset(int[] nums, BigInteger n) {
List<Integer> subset = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (n.testBit(i)) {
subset.add(nums[i]);
}
}
return subset;
}
您可以像这样访问子集:
public static void main(String... args) {
int[] nums = { 1, 2, 3, 4 };
for (BigInteger n = new BigInteger("2").pow(nums.length).subtract(BigInteger.ONE); n
.compareTo(BigInteger.ZERO) >= 0; n = n.subtract(BigInteger.ONE)) {
// process nth subset
// each subset is identified by n where 0<=n<2^nums.lenght
System.out.println(getSubset(nums, n));
}
}