在递归伪代码中,2^n 算法是
GenerateAndTest(verticesNotYetConsidered, subsetSoFar):
if verticesNotYetConsidered is empty:
yield subsetSoFar if it induces a connected subgraph
else:
choose a vertex v in verticesNotYetConsidered
GenerateAndTest(verticesNotYetConsidered - {v}, subsetSoFar)
GenerateAndTest(verticesNotYetConsidered - {v}, subsetSoFar union {v})
选择哪个顶点 v 无关紧要;我们甚至可以在两个兄弟调用中做出不同的选择。我们利用这种自由度通过修剪递归树来获得几乎线性时间的算法(n 倍于解的数量)。
如果subsetSoFar
为空,则选择仍然不受约束。否则,我们选择v
与 中的一个顶点相邻subsetSoFar
。如果不v
存在,我们让步subsetSoFar
并返回,因为在这个子树中没有其他解决方案。
请注意subsetSoFar
始终连接的新不变量,因此我们可以消除显式连接测试。我们在递归树的每个节点上做 O(n) 工作(天真 O(n^2),但我们可以增量地维护相邻顶点的集合),它是完全二元的,其叶子每个都产生一个解决方案,所以总工作如所声称的那样(回想一下内部节点的数量比叶子的数量少一)。
由于新的不变量,不会产生断开的子图。
产生每个连接的子图。对于诱导连通子图的一组顶点 S,遵循与 S 一致的分支。 S 的适当子集不可能与 S 的其余部分不相邻,因此不修剪 S。
新的伪代码如下。N(v)
表示 的邻居集合v
。
GenerateConnectedSubgraphs(verticesNotYetConsidered, subsetSoFar, neighbors):
if subsetSoFar is empty:
let candidates = verticesNotYetConsidered
else
let candidates = verticesNotYetConsidered intersect neighbors
if candidates is empty:
yield subsetSoFar
else:
choose a vertex v from candidates
GenerateConnectedSubgraphs(verticesNotYetConsidered - {v},
subsetSoFar,
neighbors)
GenerateConnectedSubgraphs(verticesNotYetConsidered - {v},
subsetSoFar union {v},
neighbors union N(v))
编辑:对于最大度数 O(1) 的图,我们可以通过保持 来实现真正的线性时间,verticesNotYetConsidered intersect neighbors
为了清楚起见,我没有这样做。如果您通过将图形表示为邻接矩阵,其中每一行存储为一个或两个字位图来利用字并行性,这种优化可能并不值得。