让G=(V,E)
成为无向图。我们如何使用以下DFS计算长度为3的循环:
DFS(G,s):
foreach v in V do
color[v] <- white; p[v] <- nil
DFS-Visit(s)
DFS-Visit(u)
color[u] <- grey
foreach v in Adj[u] do
if color[v] = white then
p[v] = u; DFS-Visit(v)
color[u] <- black
每当我们发现已经发现的节点(灰色)时,就会有一个循环。该节点的边缘称为后边缘。 p[p[p[v]]] = v
时,周期长度为3,对吧?所以
DFS-Visit(u)
color[u] <- grey
foreach v in Adj[u] do
if color[v] = grey and p[p[p[v]]] = v then
// we got a cycle of length 3
else if color[v] = white then
p[v] = u; DFS-Visit(v)
color[u] <- black
但是,我如何创建一个合适的计数器来计算周期数,以及如何只计算每个周期一次?
答案 0 :(得分:2)
我不确定您的情况parent[parent[parent[v]]] == v
是如何运作的。只要parent
表示树的结构(因为它应该对应于与DFS相关联的生成树),它就永远不应该是真的。
后边缘,交叉边缘和前边缘都可以发现&#34;新的周期。例如:
我们将以下可能性分开(让我们说你达到u -> v
边缘):
u
和v
属于同一个3周期iff parent[parent[u]] = v
。u
和v
属于同一个3周期iff parent[u] = parent[v]
。u
和v
属于同一个3周期iff parent[parent[v]] = u
。没有更多的交叉边缘。后边缘和前边缘是多余的。因此,您只需检查边缘:当您到达u -> v
后边缘时,u
和v
属于同一个3周期iff parent[parent[u]] = v
。
def dfs(u):
color[u] = GREY
for v in adj[u]:
# Back edge
if color[v] == GREY:
if parent[parent[u]] == v:
print("({}, {}, {})".format(v + 1, parent[u] + 1, u + 1))
# v unseen
elif color[v] == WHITE:
parent[v] = u
dfs(v)
color[u] = BLACK
如果你想测试它:
WHITE, GREY, BLACK = 0, 1, 2
nb_nodes, nb_edges = map(int, input().split())
adj = [[] for _ in range(nb_nodes)]
for _ in range(nb_edges):
u, v = map(int, input().split())
adj[u - 1].append(v - 1)
adj[v - 1].append(u - 1)
parent = [None] * nb_nodes
color = [WHITE] * nb_nodes
答案 1 :(得分:0)
如果没有使用DFS的解决方案没问题,可以使用O(NMlog(N³))运行一个简单的解决方案,其中N是图中顶点的数量,M是边数。
我们将迭代边缘而不是迭代顶点。对于每个边u-v,我们必须找到连接到u和v的每个顶点。我们可以通过迭代图中的每个顶点w并检查是否存在边v-w和w-u来做到这一点。每当你找到这样的顶点时,命令u,v,w并将有序三元组添加到不允许重复的BBST(例如:在C ++中使用std :: set)。在检查图中的每个边之后,长度为3个周期的计数将与BBST的大小(添加的元素数量)完全相同。
让我们分析算法的复杂性:
我们迭代每一条边。目前的复杂性是O(M)
对于每个边,我们迭代每个顶点。当前的复杂性是O(NM)
对于形成循环的每个(边,顶点)对,我们将向BBST添加三元组。添加到BBST具有O(log(K))复杂度,其中K是BST的大小。在最坏的情况下,顶点的每个三元组形成一个循环,因此我们可以向BST添加O(N³)元素,并且添加一些元素的复杂性可以高达O(log(N³))。最终的复杂性是O(NMlog(N³))。这可能听起来很多,但在最坏的情况下M = O(N²),因此复杂度将为O(N³log(N³))。由于我们可能有长达3的O(N³)个周期,因此我们的算法只是远离最优算法的对数因子。