求解有向图的强连通分量:Kosaraju算法

1、强连通分量介绍:
在有向图G中,如果两个顶点vi,vj间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)

求解强连通分量的方法如下:
2、Kosaraju算法:
算法基于的事实: 逆图G^T(同图中的每边的方向相反)和原图G有着完全相同的连通分支.也就是说,如果顶点stG中是互达的,当且仅当stG^T中也是互达的。
2.1、算法步骤及示意图:
step1:对原图G进行深度优先遍历,记录每个节点的离开时间。
step2:选择具有最晚离开时间的顶点,对反图G^T进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
step3:如果还有顶点没有删除,继续step2,否则算法结束。
图1:原始图
图1:原始图G
在这里插入图片描述
图2:图G进行dfs
在这里插入图片描述
图3:逆图G^T
图2
图4:逆图dfs,获得强连通分量==(逆图以最晚离开节点为起点进行DFS,遍历到的节点构成一个强连通分量)==

2.2、C++代码:

#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=110;
int n; // 节点个数
bool flag[MAXN];//访问标志数组
int belg[MAXN];//存储强连通分量,其中belg[i]表示顶点i属于第belg[i]个强连通分量
int numb[MAXN];//结束时间标记,其中numb[i]表示离开时间为i的顶点
int G[MAXN][MAXN],GT[MAXN][MAXN]; //邻接矩阵,逆邻接矩阵
AdjTableadj[MAXN],radj[MAXN];//邻接表,逆邻接表
//用于第一次深搜,求得numb[1..n]的值
voidVisitOne(int cur,int &sig) // 访问原图
{
    
    
    flag[cur]=true;
    for(int i=0; i<n; ++i)
        if(G[cur][i] && flag[i] == false)
            VisitOne(i,sig);
    numb[++sig]=cur; //其中numb[i]表示离开时间为i的顶点
}
//用于第二次深搜,求得belg[1..n]的值
voidVisitTwo(int cur,int count) //访问逆图
{
    
    
    flag[cur]=true;
    belg[cur]=count;
    for(int i=0; i<n; ++i)
        if(GT[cur][i] && flag[i] == false)
            VisitOne(i,count);
}
//Kosaraju算法,返回为强连通分量个数
int Kosaraju_StronglyConnectedComponent()
{
    
    
    int sig=0;// sig代表离开时间
    //第一次深搜
    memset(flag,0,sizeof(bool)*n); //访问标志设为false
    for(int i=0; i<n; i++) 
        if( flag[i] == false)
            VisitOne(i,sig);
    //第二次深搜
    memset(flag,0,sizeof(bool)*n); //访问标志设为false
    int count=0; // count为强连通分量个数
    for(int i=n; i>0; --i)
        if(flag[numb[i]] == false) //频率最高的顶点若没访问
            VisitTwo(numb[i],++count);
    return count;
}

参考资料:
a、百度百科:kosaraju算法
b、强连通分量
c、算法导论22.5节 强连通分量

3、Tarjan算法与Gabow算法
这两个我还没学,有兴趣的见链接:百度百科:强连通分量

总结:

1、Kosaraju算法:逆图以最晚离开节点为起点进行DFS,遍历到的节点构成一个强连通分量。

猜你喜欢

转载自blog.csdn.net/qq_33726635/article/details/106016480
今日推荐