铰接点算法-后边缘识别

时间:2018-12-05 06:02:40

标签: python algorithm data-structures graph depth-first-search

我正在研究Tarjan的算法,该算法使用DFS查找图形中的铰接点。

https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/

一些符号:

low[] : It is an array of N elements which stores the discovery time of every vertex. It is initialized by 0.

disc[]: It is an array of N elements which stores, for every vertex v, the discovery time of the earliest discovered vertex to which v or any of the vertices in the subtree rooted at v is having a back edge. It is initialized by INFINITY.

现在是算法:

from collections import defaultdict 

#This class represents an undirected graph 
#using adjacency list representation 
class Graph: 

    def __init__(self,vertices): 
        self.V= vertices #No. of vertices 
        self.graph = defaultdict(list) # default dictionary to store graph 
        self.Time = 0

    # function to add an edge to graph 
    def addEdge(self,u,v): 
        self.graph[u].append(v) 
        self.graph[v].append(u) 

    '''A recursive function that find articulation points 
    using DFS traversal 
    u --> The vertex to be visited next 
    visited[] --> keeps tract of visited vertices 
    disc[] --> Stores discovery times of visited vertices 
    parent[] --> Stores parent vertices in DFS tree 
    ap[] --> Store articulation points'''
    def APUtil(self,u, visited, ap, parent, low, disc): 

        #Count of children in current node 
        children =0

        # Mark the current node as visited and print it 
        visited[u]= True

        # Initialize discovery time and low value 
        disc[u] = self.Time 
        low[u] = self.Time 
        self.Time += 1

        #Recur for all the vertices adjacent to this vertex 
        for v in self.graph[u]: 
            # If v is not visited yet, then make it a child of u 
            # in DFS tree and recur for it 
            if visited[v] == False : 
                parent[v] = u 
                children += 1
                self.APUtil(v, visited, ap, parent, low, disc) 

                # Check if the subtree rooted with v has a connection to 
                # one of the ancestors of u 
                low[u] = min(low[u], low[v]) 

                # u is an articulation point in following cases 
                # (1) u is root of DFS tree and has two or more chilren. 
                if parent[u] == -1 and children > 1: 
                    ap[u] = True

                #(2) If u is not root and low value of one of its child is more 
                # than discovery value of u. 
                if parent[u] != -1 and low[v] >= disc[u]: 
                    ap[u] = True    

                # Update low value of u for parent function calls    
            elif v != parent[u]: 
                low[u] = min(low[u], disc[v]) 


    #The function to do DFS traversal. It uses recursive APUtil() 
    def AP(self): 

        # Mark all the vertices as not visited 
        # and Initialize parent and visited, 
        # and ap(articulation point) arrays 
        visited = [False] * (self.V) 
        disc = [float("Inf")] * (self.V) 
        low = [float("Inf")] * (self.V) 
        parent = [-1] * (self.V) 
        ap = [False] * (self.V) #To store articulation points 

        # Call the recursive helper function 
        # to find articulation points 
        # in DFS tree rooted with vertex 'i' 
        for i in range(self.V): 
            if visited[i] == False: 
                self.APUtil(i, visited, ap, parent, low, disc) 

        for index, value in enumerate (ap): 
            if value == True: print index, 

# Create a graph given in the above diagram 
g1 = Graph(5) 
g1.addEdge(1, 0) 
g1.addEdge(0, 2) 
g1.addEdge(2, 1) 
g1.addEdge(0, 3) 
g1.addEdge(3, 4) 

print "\nArticulation points in first graph "
g1.AP() 

g2 = Graph(4) 
g2.addEdge(0, 1) 
g2.addEdge(1, 2) 
g2.addEdge(2, 3) 
print "\nArticulation points in second graph "
g2.AP() 


g3 = Graph (7) 
g3.addEdge(0, 1) 
g3.addEdge(1, 2) 
g3.addEdge(2, 0) 
g3.addEdge(1, 3) 
g3.addEdge(1, 4) 
g3.addEdge(1, 6) 
g3.addEdge(3, 5) 
g3.addEdge(4, 5) 
print "\nArticulation points in third graph "
g3.AP() 

#This code is contributed by Neelam Yadav 

在此算法中,关注的行是:

low[u] = min(low[u], low[v]) 

这行很容易理解。 通过后缘连接到u的最早发现顶点=通过后缘连接到其任何子节点(v)的最早发现顶点

好。现在基本条件了吗?

elif v != parent[u]:  
    low[u] = min(low[u], disc[v]) 

这也很容易理解: 如果连接到u的顶点v已经“访问”(检查与此elif对应的if条件)并且“ v”不是u的父对象,则更新low [u]以包括disc [v]。

现在我的问题:

仅由于v已经被访问,所以您知道边缘(u,v)不是树的边缘。但是您如何确定这是一个后缘呢? 根据Tarjan的算法:

  

low [u] = min(disc [u],disc [w])其中w是u的祖先   是从u的后代到w的后边缘。

如果不是树形边缘,则可以是前边缘,后边缘或交叉边缘。要从这三种类型的边中识别出后边,我们需要每个顶点的开始时间和结束时间。我们在这里不做任何检查。那么我们如何假设正在进行的更新确实使用了后端呢?

1 个答案:

答案 0 :(得分:0)

您是正确的,在有向图中有四种边:树形,后边,前向和交叉边。 Wikipedia具有简要定义和说明图。但是,在无向图中,只有树和后边缘。为什么会这样?

  • 前缘(上面链接中的1-8):在有向图中,让我们考虑前缘( u v )。 DFS算法首先访问 u ,然后通过其他路径(上述链接中的1-5-6-8)到达 v ,从该路径返回,然后考虑边缘( u v )并说:“哦,我已经访问过 v ;从发现/完成时间开始,我可以看到 > v u 的后代,这意味着( u v )是前缘。”但是在无向图中,边( u v )在访问节点 v 时将首先被认为是反向。 em>,因此它成为后边缘( v u )。
  • 交叉边缘(在上面的链接中为6-3):通过类似的过程,有向图中的交叉边缘( u v )将在以下位置被发现在无向图中反转,并成为树的边缘( v u )。在示例中,我们从节点3访问节点4,然后访问节点6,该节点成为3的子节点。