使用DFS计算长度为3的循环

时间:2016-09-13 10:34:28

标签: algorithm graph-theory graph-algorithm depth-first-search

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

但是,我如何创建一个合适的计数器来计算周期数,以及如何只计算每个周期一次?

2 个答案:

答案 0 :(得分:2)

我不确定您的情况parent[parent[parent[v]]] == v是如何运作的。只要parent表示树的结构(因为它应该对应于与DFS相关联的生成树),它就永远不应该是真的。

定向图

后边缘,交叉边缘和前边缘都可以发现&#34;新的周期。例如:

Cycle with cross edge

我们将以下可能性分开(让我们说你达到u -> v边缘):

  • 后边缘:uv属于同一个3周期iff parent[parent[u]] = v
  • 交叉边缘:uv属于同一个3周期iff parent[u] = parent[v]
  • 前沿:uv属于同一个3周期iff parent[parent[v]] = u

无向图

没有更多的交叉边缘。后边缘和前边缘是多余的。因此,您只需检查边缘:当您到达u -> v后边缘时,uv属于同一个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的大小(添加的元素数量)完全相同。

让我们分析算法的复杂性:

  1. 我们迭代每一条边。目前的复杂性是O(M)

  2. 对于每个边,我们迭代每个顶点。当前的复杂性是O(NM)

  3. 对于形成循环的每个(边,顶点)对,我们将向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³)个周期,因此我们的算法只是远离最优算法的对数因子。