这是Cracking the Coding Interview 5 th 的问题9.4 问题:编写方法以返回集合的所有子集。
这是我在Java中的解决方案。(测试它,它有效!!!)
public static List<Set<Integer>> subsets(Set<Integer> s) {
Queue<Integer> copyToProtectData = new LinkedList<Integer>();
for(int member: s) {
copyToProtectData.add(member);
}
List<Set<Integer>> subsets = new ArrayList<Set<Integer>>();
generateSubsets(copyToProtectData, subsets, new HashSet<Integer>());
return subsets;
}
private static void generateSubsets(Queue<Integer> s,
List<Set<Integer>> subsets, Set<Integer> hashSet) {
if(s.isEmpty()) {
subsets.add(hashSet);
} else {
int member = s.remove();
Set<Integer> copy = new HashSet<Integer>();
for(int i:hashSet) {
copy.add(i);
}
hashSet.add(member);
Queue<Integer> queueCopy = new LinkedList<Integer>();
for(int i:s){
queueCopy.add(i);
}
generateSubsets(s, subsets, hashSet);
generateSubsets(queueCopy, subsets, copy);
}
}
我查看了这个问题的解决方案,作者说这个算法的解决方案是在 O(2 n )时间复杂度和 O(< 2 n )空间复杂性。我同意她的说法,这个算法在 O(2 n )时间运行,因为要解决这个问题,你必须考虑到这样一个事实:对于任何元素,你有两种可能性,它可以在集合中,也可以不在集合中。并且因为你有n个元素,你的问题将有2个 n 的可能性,所以问题将通过O(2 n )时间来解决。
但是我相信我有一个令人信服的论据,即我的算法在 O(n)空间中运行。我知道空间复杂度是算法相对于输入大小所占的总空间&#34; Space Complexity并且与递归调用的深度有关(请记住我观看过的一些Youtube视频)
我的一个例子是生成[1,2,3]作为[1,2,3]的子集。这是生成该组的递归调用集
generateSubsets([],子集,[1,2,3])
generateSubsets([3],子集,[1,2])
generateSubsets([2,3],子集,[1])
generateSubsets([1,2,3],子集,[])
这表明相对于原始集合大小n的递归调用的最大深度是n本身。这些递归调用中的每一个都有自己的堆栈帧。因此,我得出结论,空间复杂度是 O(n)我的证据中是否有人看到任何缺陷?
答案 0 :(得分:4)
您需要考虑算法分配的所有内存(或者更确切地说,任何时候“正在使用”的最大分配内存量) - 不仅在堆栈上,而且在堆上。每个生成的子集都存储在subsets
列表中,最终将包含2个 n 集,每个集的大小介于0和 n之间(大多数集合包含 n / 2个元素) - 所以空间复杂度实际上是 O ( n 2 < SUP> 名词 )。