1、基础知识

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

     算法描叙

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

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

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

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

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

           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(Kosaraju+强联通分量模板)

    题目链接: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. 【强联通图 | 强联通分量】HDU 1269 迷宫城堡 【Kosaraju或Tarjan算法】

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

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

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

  6. 强联通分量-tarjan算法

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

  7. 强联通分量(tarjan算法+算法简介)

    题目描述 ›对于一个有向图顶点的子集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. Dynamics CRM 2013 installation

    原创地址:http://www.cnblogs.com/jfzhu/p/3445820.html 转载请注明出处 一 硬件要求 1. CRM Server 下表对硬件的要求是假定Microsoft S ...

  2. ssh/openssh

    http://www.cnblogs.com/wwufengg/articles/ssh-openssh-detail.html http://www.cnblogs.com/jjkv3/archiv ...

  3. 开发人员应该对IIS理论层的知识了解的多一些~第四讲 HttpModule中的几大事件

    返回目录 本文主要介绍HttpModule,它在一个网页请求过程中是一个怎样的过程是我们要知道的,在网页加载过程中HttpModule在何时被执行也是我们要知道的,以及,HttpModule在网页请求 ...

  4. .NetCore~Linux环境下部署

    NetCore正式版已经出现有段时候了,Windows下使用vs2015开发.netCore应用程序,然后通过dotnet程序开启WEB服务,用着很像node.js,当然我们不会于只局限于window ...

  5. Windows hosts (使用方法 && 不定期更新)

    Windows 系统hosts位于 C:\Windows\System32\drivers\etc\hosts 使用方法:删除原来的hosts文件(不放心可以剪切到其他路径备份),然后将本文链接里的h ...

  6. Android上dip、dp、px、sp等单位说明

    Android上dip.dp.px.sp等单位说明 dip  device independent pixels(设备独立像素). 不同设备不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA ...

  7. 基于 flow.ci 实现 PHP 项目自动化持续集成

    高效程序员的习惯之一--让开发流程自动化.Automating shapes smarter future. 这是一个关于如何快速实现 PHP 项目自动化持续集成的快速指导.无论你是否使用过持续集成, ...

  8. Unity3D重要知识点

    数据结构和算法很重要!图形学也很重要!大的游戏公司很看重个人基础,综合能力小公司看你实际工作能力,看你的Demo. 1.什么是渲染管道? 是指在显示器上为了显示出图像而经过的一系列必要操作. 渲染管道 ...

  9. JavaWeb:JavaBean基础

    JavaBean基础 JavaBean简介: 1.JavaBean是一种可以重复使用的类,可以没有用户界面,主要负责业务数据或者处理事物(数据运算.操作数据库) 2.与JSP配合,可以简化JSP代码. ...

  10. [转载]TFS发送邮件提醒功能

    第一次使用TFS 2010,发现有Project Alerts功能,就是项目组工程中若有任何改动时,TFS Server会自动发邮件提醒.Microsoft提供的配置方法(http://msdn.mi ...