https://github.com/yaojingguo/subsets给出了解决子集问题的两种算法。Iter
算法与问题中给出的代码相同。Recur
算法使用递归来访问每个可能的子集。两种算法的时间复杂度都是Θ(n*2^n)
。在Iter
算法中,1
语句执行n*2^n
次数。2
语句执行n*2^(n-1)
(基于@templatetypedef 的分析)。用于a
表示 的成本1
。并b
用来表示成本2
。总成本为n*2^n*a + n*2^(n-1)*b
。
if ((i & (1 << j)) > 0) // 1
list.add(A[j]); // 2
下面是Recur
算法的主要逻辑:
result.add(new ArrayList<Integer>(list)); // 3
for (int i = pos; i < num.length; i++) { // 4
list.add(num[i]);
dfs(result, list, num, i + 1);
list.remove(list.size() - 1);
}
语句3
的成本n*2^(n-1)*b
与1
. 另一个成本是4
循环。每个循环迭代包括三个函数调用。4
总共执行2^n
次数。用于d
表示 的成本4
。总成本为2^n*d + n*2^(n-1)*b
。下图是该算法的递归树 set {1, 2, 3, 4}
。更精确的分析需要以2^(n-1)
不同方式处理叶节点。
Ø --- 1 --- 2 --- 3 --- 4
| | |- 4
| |- 3 --- 4
| |- 4
|- 2 --- 3 --- 4
| |- 4
|- 3 --- 4
|- 4
比较这两种算法的复杂度就是比较n*2^n*a
(1)和2^n*d
(2)。将 (1) 除以 (2),我们有n * a / d
。如果n*a
小于d
,Iter
则比 快Recur
。我用来Driver
对这两种算法的效率进行基准测试。这是一次运行的结果:
n: 16
Iter mills: 40
Recur mills: 19
n: 17
Iter mills: 78
Recur mills: 32
n: 18
Iter mills: 112
Recur mills: 10
n: 19
Iter mills: 156
Recur mills: 149
n: 20
Iter mills: 563
Recur mills: 164
n: 21
Iter mills: 2423
Recur mills: 1149
n: 22
Iter mills: 7402
Recur mills: 2211
它表明,对于小n
,Recur
比Iter
。