Kosaraju算法---强联通分量

时间:2022-09-02 19:37:38

1、基础知识

所需结构:原图、反向图(若在原图中存在vi到vj有向边,在反向图中就变为vj到vi的有向边)、标记数组(标记是否遍历过)、一个栈(或记录顶点离开时间的数组)。

     算法描叙

步骤1:对原图进行深度优先遍历,记录每个顶点的离开时间。

步骤2:选择具有最晚离开时间的顶点,对反向图进行深度优先遍历,并标记能够遍历到的顶点,这些顶点构成一个强连通分量。

步骤3:如果还有顶点没有遍历过,则继续进行步骤2,否则算法结束。

在dfs(bfs)中,一个结点的开始访问时间指的是遍历时首次遇到该结点的时间,而该结点的结束访问时间则指的是将其所有邻接结点均访问完的时间。

下面结合实例说明Kosaraju算法的基本策略。图1给出了一个有向图G。

Kosaraju算法---强联通分量

           Step1:假设从DFS在遍历时按照字母顺序进行,从4开始搜索得到以下搜索序列:[4,[5,[7,[6,[8,8],6],7],5],4]  [1,[2,[3,3],2],1] 在遍历序列中每一个结点出现了两次,其中第一次出现的时间为该结点的开始访问时间,第二次出现的时间为该结点的结束访问时间。

根据结点结束访问时间的定义,在某一次调用DFS_SEARCH中,显然遍历时越早遇到的结点,相对结束访问时间也就越晚,因此在这一次调用DFS_SEARCH内结点结束的时间递减顺序与结点首次访问顺序一致。而对于不同次调用DFS_SEARCH,调用越晚其相对结点结束的时间也就越晚。根据上述分析,我们可以在第一次DFS遍历时一次得到内结点结束的时间的递减顺序。

 1)该图得到一个搜索树林共两个搜索树,一个以4为根(4能到达的所有点),一个以1为根(1能到达的所有点)。

 2)4能到的所有点在搜索序列里挨一起,4不能到的点形成的搜索序列肯定在其右边。

 3)搜索序列的左右括号是成对出现的,满足括号匹配的原则。某个节点两端括住的节点都是他能到达的所有结点。最先开始访问的节点(左括号),肯定也是其搜在              子树里面最后一个访问的节点(右括号)。

         Step2:对图进行反向,再从1(4)[先1后4]点进行dfs,相当于在反向前的原图中求出所有能到达1(4)的点。

         Step3:按结束时间从大到小进行搜索。我们看搜索序列,最后一个点肯定是第一遍深搜时得到的最后一棵树的根r。从它开始深搜,相当于求出所有能达到树根r的点。这样得到的搜索子树肯定是一个强联通分量。

图的存储表示形式:邻接矩阵[1,2标记原图和逆图的边]、邻接表。

Kosaraju算法的显著特征是:第一,引用了有向图的逆图;为了突出回向边,而对图进行转置,然后对转置的图按照之前得到的顶点序列进行DFS调用。第二,需要 对图进行两次DFS(一次在逆图上,一次在原图上)。而且这个算法依赖于一个事实:一个有向图的强连通分量与其逆图是一样的。由于算法的时间取决于两次DFS,因           此时间复杂度,对于稀疏图是O(V+E),对于稠密图是O(V²),可见这是一个线性算法。Kosaraju的结论是,在第二次DFS中,同一棵搜索树上的结点属于一个强连通分量。

与Tarjan算法的比较

在实际的测试中,时间:Tarjan算法的运行效率也比Kosaraju算法高30%左右。空间:Tarjan算法虽然比Kosaraju算法多用两个一维数组,但Kosaraju算法比Tarjan算法 多浪费建立一个反向图的空间,所以总体来说在空间复杂度上Tarjan比Kosaraju占优势。

隐藏性质:在第二次深搜选择树的顺序,若把求出来的每个强连通分量收缩成一个点 ,并且用求出每个强连通分量的顺序来标记收缩后的节点,那么这个顺序其实就是强连通分量收缩成点后形成的有向无环图的拓扑序列。

2、参考代码

 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXL 100 /*图的邻接表定义*/
typedef struct edge_node{ //边NODE
int key;
struct edge_node *next;
}ENode;
typedef struct{ //顶点
char vertex;
ENode *firstedge;
}VNode;
typedef VNode VList[MAXL]; //顶点表
typedef struct{ //图
// int n,e;
VList vlist;
}ALGraph;
ALGraph *ALG=(ALGraph *)malloc(sizeof(ALGraph)); //原图邻接表
ALGraph *reverse_ALG=(ALGraph *)malloc(sizeof(ALGraph)); //逆图邻接表
int n,e; //两表共用 int vis[MAXL]; int num_scc;
int scc[MAXL]; int cnt;
int ord[MAXL]; void Creat_ALGraph(void) //建图[邻接表和逆邻接表]
{
int i,j;
char ch1,ch2;
ENode *ptr=(ENode *)malloc(sizeof(ENode)); scanf("%d,%d",&n,&e);
getchar(); for(i=;i<n;i++)
{
scanf("%c",&ALG->vlist[i].vertex);
getchar();
ALG->vlist[i].firstedge=NULL; reverse_ALG->vlist[i].vertex=ALG->vlist[i].vertex; //逆表
reverse_ALG->vlist[i].firstedge=NULL;
}
for(int k=;k<e;k++)
{
scanf("%c,%c",&ch1,&ch2);
getchar(); for(i=;ch1!=ALG->vlist[i].vertex;i++);
for(j=;ch2!=ALG->vlist[j].vertex;j++); ptr=(ENode *)malloc(sizeof(ENode));
ptr->key=j;
ptr->next=ALG->vlist[i].firstedge;
ALG->vlist[i].firstedge=ptr; ptr=(ENode *)malloc(sizeof(ENode)); //逆表
ptr->key=i;
ptr->next=reverse_ALG->vlist[j].firstedge;
reverse_ALG->vlist[j].firstedge=ptr;
}
} void init_kosaraju(void)
{
num_scc=;
cnt=;
for(int i=;i<n;i++)
{
vis[i]=;
scc[i]=-;
ord[i]=-;
}
} /*****************Kosaraju算法******************/
void orig_DFS(int u)
{
int son;
ENode *ptr=(ENode *)malloc(sizeof(ENode)); vis[u]=; //标记+遍历+入栈
ptr=ALG->vlist[u].firstedge;
while(ptr!=NULL)
{
son=ptr->key;
if(!vis[son])
orig_DFS(son);
ptr=ptr->next;
}
ord[cnt++]=u;
} void reverse_DFS(int u)
{
int par;
ENode *ptr=(ENode *)malloc(sizeof(ENode)); scc[u]=num_scc; //归类+标记+遍历
vis[u]=;
ptr=reverse_ALG->vlist[u].firstedge;
while(ptr!=NULL)
{
par=ptr->key;
if(!vis[par])
reverse_DFS(par);
ptr=ptr->next;
}
} void SCC_Kosaraju()
{
int i; for(i=;i<n;i++)
if(!vis[i])
orig_DFS(i); memset(vis,,sizeof(vis)); for(i=n-;i>=;i--)
if(!vis[ord[i]])
{
num_scc++;
reverse_DFS(ord[i]);
}
}
/*********************************************/ int main(void)
{
Creat_ALGraph(); init_kosaraju(); SCC_Kosaraju(); printf("%d\n",num_scc); //scc输出
for(int cn=;cn<=num_scc;cn++)
{
for(int j=;j<n;j++)
if(scc[j] == cn)
printf("%c ",ALG->vlist[j].vertex); //ALG、reverse_ALG均可
printf("\n");
} return ;
}

Kosaraju算法---强联通分量的更多相关文章

  1. Tarjan算法---强联通分量

    1.基础知识 在有向图G,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子 ...

  2. POJ 2186 Popular cows&lpar;Kosaraju&plus;强联通分量模板)

    题目链接:http://poj.org/problem?id=2186 题目大意:给定N头牛和M个有序对(A,B),(A,B)表示A牛认为B牛是红人,该关系具有传递性,如果牛A认为牛B是红人,牛B认为 ...

  3. 强联通分量之kosaraju算法

    首先定义:强联通分量是有向图G=(V, E)的最大结点集合,满足该集合中的任意一对结点v和u,路径vu和uv同时存在. kosaraju算法用来寻找强联通分量.对于图G,它首先随便找个结点dfs,求出 ...

  4. 【强联通图 &vert; 强联通分量】HDU 1269 迷宫城堡 【Kosaraju或Tarjan算法】

      为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明 ...

  5. POJ 2186-Popular Cows &lpar;图论-强联通分量Korasaju算法)

    题目链接:http://poj.org/problem?id=2186 题目大意:有n头牛和m对关系, 每一对关系有两个数(a, b)代表a牛认为b牛是“受欢迎”的,且这种关系具有传递性, 如果a牛认 ...

  6. 强联通分量-tarjan算法

    定义:在一张有向图中,两个点可以相互到达,则称这两个点强连通:一张有向图上任意两个点可以相互到达,则称这张图为强连通图:非强连通图有极大的强连通子图,成为强联通分量. 如图,{1},{6}分别是一个强 ...

  7. 强联通分量(tarjan算法&plus;算法简介)

    题目描述 ›对于一个有向图顶点的子集S,如果在S内任取两个顶点u和v,都能找到一条从u到v的路径,那么就称S是强连通的.如果在强连通的顶点集合S中加入其他任意顶点集合后,它都不再是强连通的,那么就称S ...

  8. Tarjan 算法求割点、 割边、 强联通分量

    Tarjan算法是一个基于dfs的搜索算法, 可以在O(N+M)的复杂度内求出图的割点.割边和强联通分量等信息. https://www.cnblogs.com/shadowland/p/587225 ...

  9. 【POJ 1236 Network of Schools】强联通分量问题 Tarjan算法,缩点

    题目链接:http://poj.org/problem?id=1236 题意:给定一个表示n所学校网络连通关系的有向图.现要通过网络分发软件,规则是:若顶点u,v存在通路,发给u,则v可以通过网络从u ...

随机推荐

  1. python 3&period;5&period;2 install pillow

    1. 首先尝试从官网下载, pip install pillow, 结果网络不行,总是连不上或者下载中就失败, C:\Windows\system32>pip install pillowCol ...

  2. js归并排序法

    function mergeSort(arr) { var len = arr.length; if(len > 1) { var index = Math.floor(len / 2); le ...

  3. Tomcat源码解读:ClassLoader的设计

    Tomcat是一个经典的web server,学习tomcat的源码对于我们是有很大的帮助的.前一段时间了解了tomcat的工作的大致流程,对我的新工作有了很大的帮助.刚学习了ClassLoader( ...

  4. MySQL root密码重置 报错:mysqladmin&colon; connect to server at &&num;39&semi;localhost&&num;39&semi; failed的解决方案

    ===========================================================二,忘记本地root的登录密码解决过程:1.编辑/mysql/my.ini在[my ...

  5. java 获取实体类对象属性值的方法

    在java中我们要获得实体类对象的属性,一般情况是将实体类中的属性私有化,然后再对外提供get()与set()方法,然后再获取实体类对象的属性的时候先把对象new出来,再用变量名.get()的方法得到 ...

  6. Headfirst设计模式的C&plus;&plus;实现——复合模式

    observer.h #ifndef _OBSERVER_H_ #define _OBSERVER_H_ #include <string> class Observer { public ...

  7. 【转载】c&plus;&plus;中的 extern &quot&semi;C&quot&semi;&lpar;讲的更好一些&rpar;

    [说明]本文章转载自 东边日出西边雨 的文章http://songpengfei.iteye.com/blog/1100239 ------------------------------------ ...

  8. &lbrack;原创&rsqb; 绿色单文件封装程序GreenOne V3&period;0

    1.原理 将包含可执行文件的多个文件 调用Winrar,创建自解压格式压缩文件 设置高级自解压选项中的文本和图标,设置解压后运行的文件为选中的可执行文件. 这种创建单文件封装其实也就是一个Winrar ...

  9. Hello Kiki(中国剩余定理——不互质的情况)

    Hello Kiki Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Su ...

  10. 4th Dec 2018

    两个人都觉得自己没错,或者对方都把错误归结于另外一方,总会盯着对方的不足,无限放大:谁都不肯先放下兵器,亦或害怕自己放下了兵器,对方又拿起了盾.这就好像双方在同一时间拉扯一根皮筋,拉扯的越长,张力越大 ...