图--强连通分支

图–强连通分支

  1. 我们关注一下互联网相关的非常巨大图

    1. 由主机通过网线(或无线)连接而形成的图
    2. 以及由网页通过超链接连接而形成的图
      1. 以网页(URI作为id)为顶点,网页内包含的超链接作为边,可以转换为一个有向图
  2. 我们可以猜想,web的底层结构可能存在某些同类网站的聚集

  3. 在图中发现高度聚集节点群的算法,即寻找"强连通分支"算法

  4. 强连通分支,定义为图G的一个子集C

    1. C中的任意两个顶点v,w之间都有路径来回,即(v,w) (w,v)都是C的路径,而且C是具有这样性质的最大子集

    2. 下图是具有3个强连通分支的9顶点有向图

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0GDeBdd5-1614326661677)(C:\Users\93623\AppData\Roaming\Typora\typora-user-images\image-20210221171452699.png)]

    3. 一旦找到强连通分支,可以据此对图的顶点进行分类,并对图进行化简

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YujHdPgm-1614326661680)(C:\Users\93623\AppData\Roaming\Typora\typora-user-images\image-20210221171549418.png)]

  5. 利用深度优先搜索,我们可以再次创建强大的算法

  6. 在用深度优先搜索来发现强连通分支之前,先熟悉一个概念: Transposition转置

    一个有向图G的转置 G T \mathbf{G}^{\boldsymbol{T}} GT,定义为将图G的所有边的顶点交换次序,如将(v,w)转换为(w,v)

    可以观察到图和转置图在强连通分支的数量和划分上,是相同的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GuUoQ8gR-1614326661681)(C:\Users\93623\AppData\Roaming\Typora\typora-user-images\image-20210221172051718.png)]

  7. 强连通分支算法: Kosaraju算法思路

    1. 对图G调用DFS算法,为每个顶点计算"结束时间";
    2. 然后,将图G进行转置,得到 G T \mathbf{G}^{\boldsymbol{T}} GT,
    3. 再对 G T \mathbf{G}^{\boldsymbol{T}} GT调用DFS算法,但在dfs函数中,对每个顶点的搜索循环里,要以顶点的"结束时间"倒序的顺序来搜索
    4. 最后,深度优先森林中的每一棵树就是一个强连通分支
# coding: utf-8

# from pythonds.graphs import Graph
import graph_ccc


# 通用深度优先搜索
class DFSGraph(graph_ccc.Graph):
    """
    time, 发现时间,结束时间对于深度遍历是没有用的
    主要是用于强连通和拓扑
    """

    def __init__(self):
        super().__init__()
        self.time = 0

    def dfs(self):
        # 颜色的初始化, 将所有顶点的颜色都置为白色,前驱都为-1
        for aVertex in self:
            aVertex.setColor('white')
            aVertex.setPred(-1)
        for aVertex in self:
            # 如果还有未包括的顶点,则建森林
            if aVertex.getColor() == 'white':
                # print("wwwwwwwwww",aVertex.getId())
                self.dfsvisit(aVertex)

    def dfsEx(self):
        # 颜色的初始化, 将所有顶点的颜色都置为白色,前驱都为-1
        for aVertex in self:
            aVertex.setColor('white')
            aVertex.setPred(-1)
        lst = list(self.vertList.values())
        lst.sort(key=lambda v: -v.fin)
        mydict = {
    
    }
        for v in lst:
            # 如果还有未包括的顶点,则建森林
            if v.getColor() == 'white':
                # print("wwwwwwwwww",aVertex.getId())
                mydict[v.getId()] = []
                self.dfsvisitEx(v, mydict[v.getId()])
        return mydict

    def dfsvisitEx(self, startVertex, path):
        path.append(startVertex)
        startVertex.setColor('gray')
        # 算法的步数加一
        self.time += 1
        startVertex.setDiscovery(self.time)
        for nextVertex in startVertex.getConnections():
            if nextVertex.getColor() == 'white':
                nextVertex.setPred(startVertex)
                # 深度优先递归访问
                self.dfsvisitEx(nextVertex, path)
        startVertex.setColor('black')
        self.time += 1
        startVertex.setFinish(self.time)
        print(startVertex.getId(), end=" ")

    def dfsvisit(self, startVertex):

        startVertex.setColor('gray')
        # 算法的步数加一
        self.time += 1
        startVertex.setDiscovery(self.time)
        for nextVertex in startVertex.getConnections():
            if nextVertex.getColor() == 'white':
                nextVertex.setPred(startVertex)
                # 深度优先递归访问
                self.dfsvisit(nextVertex)
        startVertex.setColor('black')
        self.time += 1
        startVertex.setFinish(self.time)
        print(startVertex.getId(), end=" ")


if __name__ == '__main__':
    g = DFSGraph()
    # g.addVertex('A')
    # g.addVertex('B')
    # g.addVertex('C')
    # g.addVertex('D')
    # g.addVertex('E')
    # g.addVertex('F')
    # g.addVertex('G')
    # g.addVertex('H')
    # g.addVertex('I')
    g.addEdge('A', 'B')
    g.addEdge('B', 'E')
    g.addEdge('E', 'D')
    g.addEdge('D', 'B')
    g.addEdge('E', 'A')
    g.addEdge('D', 'G')
    g.addEdge('G', 'E')
    g.addEdge('B', 'C')
    g.addEdge('C', 'F')
    g.addEdge('F', 'H')
    g.addEdge('H', 'I')
    g.addEdge('I', 'F')
    
    # 1. 对图调用DFS算法,为每个顶点计算"结束时间"
    g.dfs()
    print("\n")
    for node in g:
        print(node, node.disc, node.fin)
    
    # 2.对图进行转置操作
    gt = DFSGraph()
    for node in g:
        for nbr in node.connectedTo.keys():
            gt.addEdge(nbr.getId(), node.getId())
            # 保留以前遍历出来的开始时间和结束时间
            start = gt.getVertex(nbr.getId())
            start.disc = nbr.disc
            start.fin = nbr.fin

            end = gt.getVertex(node.getId())
            end.disc = node.disc
            end.fin = node.fin

    #     print(node.getId(), node.getFinish(), node.getDiscovery())
    print("=" * 30)

    for node in gt:
        print(node, node.disc, node.fin)
    # 3.对转置图调用dfs算法,对每个顶点的搜索循环里,要以顶点"结束时间倒序的顺序来搜索
    mydict = gt.dfsEx()
    print("=" * 70)
    # print(mydict)
    # 4. 最后,深度优先森林中的每一棵树都是一个强连通分支
    """
    A,E,B,D,G,
    C,
    F,I,H,
    """
    for item in mydict.items():
        for node in item[1]:
            print(node.getId(), end=",")
        print()

猜你喜欢

转载自blog.csdn.net/weixin_46129834/article/details/114137463