有向图的强连通性 & Kosaraju算法(求有向图的强连通分量)

浅谈无向图的连通性

连通图是无向图的一个概念

  • 在无向图中,若从顶点 v 1 v_1 v1 到顶点 v 2 v_2 v2 有路径,则称顶点 v 1 v_1 v1 v 2 v_2 v2 是连通的;
  • 如果图中任意一对顶点都是连通的,则称此无向图是连通图

对于一个非连通图,可以通过深度优先搜索或广度优先搜索来获取它的连通分量:从一个未访问过的节点开始进行深度优先或者广度优先搜索,其能到达的所有顶点及其相关的边构成的子图就是一个连通分量;直到访问完全部的节点,便可以获取该图的所有连通分量。

有向图强连通性的概念

在有向图 G = ( V , E ) G=(V,E) G=(V,E) 中,若对于每一对顶点 v 1 v_1 v1 v 2 v_2 v2,都存在一条从 v 1 v_1 v1 v 2 v_2 v2 和从 v 2 v_2 v2 v 1 v_1 v1 的路径,则称此图是强连通图

相应地有 强连通分量(极大强连通子图) 的概念:设 E i ( 1 ≤ i ≤ r ) E_i(1\leq i \leq r) Ei(1ir) 是头、尾均在 V i V_i Vi 中的边集,则子图 G i ( V i , E i ) G_i(V_i,E_i) Gi(Vi,Ei)称为 G G G 的一个强连通分量,简称强分量、强支

强连通图只有一个强连通分量,即其自身;非强连通的有向图有多个强连通分量。

在这里插入图片描述

如上图可以分为 5 个强连通分量。

设从 v v v 可到达(以 v v v 为起点的所有有向路径的终点)的顶点集合为 T 1 ( G ) T_1(G) T1(G),而到达 v v v(以 v v v 为终点的所有有向路径的起点)的顶点集合为 T 2 ( G ) T_2(G) T2(G),则包含 v v v 的强连通分量的顶点集合是: T 1 ( G ) ∩ T 2 ( G ) T_1(G)∩T_2(G) T1(G)T2(G)

强连通图的性质定理

定理:一个有向图是强连通的,当且仅当 G G G 中有一个回路,它至少包含每个顶点一次。

  • 充分性:如果 G G G 中有一个回路,它至少包含每个顶点一次,则 G G G 中任两个顶点都是互相可达的,故 G G G 是强连通图。
  • 必要性:如果有向图是强连通的,则任两个顶点都是相互可达,故必可做一回路经过图中所有顶点。若不然则必有一回路不包含某一顶点 v v v,并且 v v v 与回路上的各顶点就不是相互可达,与强连通条件矛盾。

求有向图强连通分量—Korasaju算法

在计算科学中,Kosaraju的算法(又称为 Sharir Kosaraju算法)是一个线性时间(linear time)算法找到的有向图的强连通分量。它利用了一个事实,逆图(与各边方向相同的图形反转, transpose graph)有相同的强连通分量的原始图。

算法步骤:

  • (1)深度优先遍历 G G G(起点如何选择无所谓),并计算出 每 个 顶 点 u 的 结 束 时 间 d f n [ u ] \color{red}{每个顶点 u 的结束时间 dfn[u]} udfn[u](即按递归写法出系统栈的顺序编号,完成时间这个编号和深度优先遍历序列是不同的);
  • (2)深度优先遍历 G G G 的转置(反向)图 G T G^T GT,选择遍历的起点时,按照顶点的结束时间从大到小进行。遍历的过程中,一边遍历,一边给顶点做分类标记,每找到一个新的起点,分类标记值就加 1。
  • (3)第(2)步中产生的标记值相同的顶点构成深度优先森林中的一棵树,也即一个强连通分量。

这里提出了 每 个 顶 点 的 结 束 时 间 \color{red}{每个顶点的结束时间} 这个概念,这个编号和深度优先搜索的编号是不同的,在拓扑排序的 DFS实现(递归)中也用到了每个顶点的结束时间。

在这里插入图片描述
在上图中,图 ( a ) (a) (a) 表示有向图 G G G,要求其强连通分量:

  • 假设从 ( a ) (a) (a) 开始进行深度优先搜索(其实选哪个点都无所谓,最后得到的强连通分量是相通的),记录每个顶点的结束时间,结束时间如 ( b ) (b) (b) 所示;
  • 然后对 G G G 进行反向得到 G T G^T GT,按上一步得到的结束时间从大到小再对 G T G^T GT 进行深度优先搜索(普通的深度优先搜索):
    • 先从结束时间最晚的 a a a 开始搜,可以搜索到 a , d , c a,d,c a,d,c 三个节点,所以将 a , d , c a,d,c a,d,c 最为一个强连通分量中的节点;
    • 目前未访问的且结束时间最晚的节点是 b b b,所以再从 b b b 开始对 G T G^T GT 进行深度优先搜索,可以搜到 b , e , f b,e,f b,e,f,所以将 b , e , f b,e,f b,e,f 作为一个强连通分量中的节点;
    • 至此,图 G T G^T GT 中没有未访问的节点,算法结束,共两个强连通分量,如 ( d ) (d) (d) 所示。

伪代码:

/* 按弧的正向搜索,起点如何选择无所谓 */
int in_order[MAX_VEX] ;
void DFS(OLGraph *G , int v)  	 // 求每个节点的结束时间
{ 
    ArcNode *p;
    Count = 0;
    visited[v] = TRUE;
    for(p=G->xlist[v].firstout; p!=NULL; p=p->tlink)
        if(!visited[p->headvex])
            DFS(G, p->headvex);
    in_order[count++]=v; 		 // 出系统栈的顺序,不是真的深度优先搜索序列
} 

/* 对图G按弧的逆向进行搜索 */
void Rev_DFS(OLGraph *G , int v)
{ 
    ArcNode *p;
    visited[v] = TRUE;
    printf(“%d”, v); /* 输出顶点 */
    for(p=G->xlist[v].firstin; p!=NULL; p=p->hlink)
        if(!visited[p->tailvex])
            Rev_DFS(G, p->tailvex);
} 

void Strongly_Connected_Component(OLGraph *G)
{ 
    int k = 1, v, j;
    
    /* 对图G正向遍历 */
    for (v=0; v<G->vexnum; v++) 
        visited[v] = FALSE;
    for (v=0; v<G->vexnum; v++)
        if(!visited[v]) 
            DFS(G, v);

    /* 对图G逆向遍历 */
    for (v=0; v<G->vexnum; v++) 
        visited[v] = FALSE;
    for (j=G->vexnum-1; j>=0; j--) { 
        v = in_order[j];
        if (!visited[v]) {
            // 从这个点开始的深度优先搜索能搜到的节点都属于一个连通分量
            printf(“\n第%d个连通分量顶点: ”, k++);
            Rev_DFS(G, v);
        }
    }
}

参考

https://blog.csdn.net/yjw123456/article/details/90447207

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值