寻找有向图的强连通分支

求有向图G的强连通分支可以通过下面的步骤进行:

(1)对G执行深度优先搜索,求出每个顶点的后序遍历顺序号postOrder。

(2)反转有向图G中的边,构造一个新的有向图G*。

(3)由最高的postOrder编号开始,对G*执行深度优先搜索。如果深度优先搜索未达到所有顶点,由未访问的最高postOrder编号的顶点开始,继续深度优先搜索。

(4)步骤三所产生的森林中的每一棵树,对应于一个强连通分支。

      但是为什么通过上面的步骤就会得到强连通分支呢?在网上搜索了一下,却得到了一大堆定理的证明,一看头就晕了。我还是喜欢从习惯上面去理解这些复杂的东西,下面就先分析一下吧。

      首先假设经过对图G深度优先搜索按顺序形成的森林为a,b,c,……。那么一定有(1)一个强连通分支一定在森林中的一个树上面,而不会跨越一个树到另一个树上面。证明很简单,假设说某一强连通分支跨越了两个树,那么在对图G进行深度优先搜索的时候,一定可以从a上的某一个节点访问到b上的某个节点,也就是说深搜之后a,b应该是一棵树,这就矛盾了。(2)将一个强连通分支各个边反向后,还是一个强连通分支。这个是显然的。(3)经过对G图的深搜,一定不存在从先生成的树到后生成的树的边,但是反过来是有可能成立的。原因和证明第一个结论是一样的。而后序遍历顺序号一定是后生成的树中的节点要大,所以再根据postOrder对G*图深搜时根据第三条结论,一定不会从一棵树访问到另一棵树上面去。这就保证了可以满足第一个结论。

      最后要解决的问题就是如何保证按步骤三搜索得到的一定是强连通图,这也是我最不知道怎么解释的地方了。根据我的理解,深搜得到的一棵树从上往下一定是可达的,所以反向图从下往上一定是可达的。而根据步骤一得到的后序遍历顺序号一定是从上往下编号从大到小,那么如果反向图还能从树根访问到其他的节点就应该能说明原图这些节点是互相可以走通的。这也是postOrder的另一个作用。下面还是在代码上加上注释,结合代码应该会更好理解一些。

//MAX在前面自定义,这里的代码没有记录任何信息,需要根据实际题目添加代码
//graph和reGraph分别为原图G和边反向后的新图G*的邻接表
bool isUsed[MAX];      //记录节点是否被访问过
int postOrder[MAX];    //记录节点后序遍历的编号
int decVer[MAX];        //postOrder为下标记录节点
void StronglyConn(Node *graph,int n)
{
    int index=0;
    for(i=1;i<=n;i++)isUsed[i]=false;
    for(i=1;i<=n;i++)
    {
        if(!isUsed[i])DFS_Search(i,graph,postOrder,index);   //对G深搜得到postOrder
    }
    for(i=1;i<=n;i++)
    {
        decVer[postOrder[i]]=i;    //第postOrder[i]个节点是i
        isUsed[i]=false;
    }
    for(i=n;i>0;i--)       //从postOrder最大的开始对G*深搜,在这里可能要记录些信息
    {
        if(!isUsed[decVer[i]])DFS_Search(decVer[i],reGraph,postOrder,index);
    }
}
void DFS_Search(int from,Node *g,int *order,int &index)
{
    Node *p=g[from].next;
    isUsed[from]=true;
    while(p)
    {
        if(!isUsed[p->ver])DFS_Search(p->ver,g,order,index);
        p=p->next;
    }
    order[from]=++index;   //深搜的同时记录后序遍历序号
}

 

补充:

1.从G中取任意节点s并从s开始运行BFS,然后也在G的反向图中从s开始运行BFS,如果两个搜索中有一个没有成功到达每一个节点,那么显然G就不会是一个强连通图。反之,可以得到s到每一个节点有一条路径,并且每个节点有一条路径到达s,那么图G一定是强连通图。

你可能感兴趣的:(读书笔记)