Kosaraju算法---强联通分量
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算法---强联通分量的更多相关文章
- Tarjan算法---强联通分量
1.基础知识 在有向图G,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子 ...
- POJ 2186 Popular cows(Kosaraju+强联通分量模板)
题目链接:http://poj.org/problem?id=2186 题目大意:给定N头牛和M个有序对(A,B),(A,B)表示A牛认为B牛是红人,该关系具有传递性,如果牛A认为牛B是红人,牛B认为 ...
- 强联通分量之kosaraju算法
首先定义:强联通分量是有向图G=(V, E)的最大结点集合,满足该集合中的任意一对结点v和u,路径vu和uv同时存在. kosaraju算法用来寻找强联通分量.对于图G,它首先随便找个结点dfs,求出 ...
- 【强联通图 | 强联通分量】HDU 1269 迷宫城堡 【Kosaraju或Tarjan算法】
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明 ...
- POJ 2186-Popular Cows (图论-强联通分量Korasaju算法)
题目链接:http://poj.org/problem?id=2186 题目大意:有n头牛和m对关系, 每一对关系有两个数(a, b)代表a牛认为b牛是“受欢迎”的,且这种关系具有传递性, 如果a牛认 ...
- 强联通分量-tarjan算法
定义:在一张有向图中,两个点可以相互到达,则称这两个点强连通:一张有向图上任意两个点可以相互到达,则称这张图为强连通图:非强连通图有极大的强连通子图,成为强联通分量. 如图,{1},{6}分别是一个强 ...
- 强联通分量(tarjan算法+算法简介)
题目描述 对于一个有向图顶点的子集S,如果在S内任取两个顶点u和v,都能找到一条从u到v的路径,那么就称S是强连通的.如果在强连通的顶点集合S中加入其他任意顶点集合后,它都不再是强连通的,那么就称S ...
- Tarjan 算法求割点、 割边、 强联通分量
Tarjan算法是一个基于dfs的搜索算法, 可以在O(N+M)的复杂度内求出图的割点.割边和强联通分量等信息. https://www.cnblogs.com/shadowland/p/587225 ...
- 【POJ 1236 Network of Schools】强联通分量问题 Tarjan算法,缩点
题目链接:http://poj.org/problem?id=1236 题意:给定一个表示n所学校网络连通关系的有向图.现要通过网络分发软件,规则是:若顶点u,v存在通路,发给u,则v可以通过网络从u ...
随机推荐
- Windows桌面共享中一些常见的抓屏技术
1. BitBlt 我想做Windows开发应该都知道这个API, 它能实现DC间的内容拷贝, 如果我们把源DC指定成Monitor DC或是桌面DC, 它就能实现抓屏功能. 对于通过这种方式的抓屏, ...
- http学习笔记(四)——HTTP报文
http报文是在http应用程序之间发送的数据块,这些数据块以一些文本形式的元信息. 请求报文从客户端流入服务器,向服务器请求数据,服务器响应请求,响应报文从服务器流出,回到客户端. 这就构成了一个事 ...
- 可拖动的DIV
在做WEB UI设计的时候,拖动某个HTML元素已经成为一种不能忽视的用户界面模式,比较典型的应用例子就是Dialog,一个元素是怎么实现拖动的呢?其实原理非常简单,要想实现首先得了解几个基本知识. ...
- git rm–r folder fatal:pathspec "" did not match any files
问题描述: 某年某月某日,在查看git库的时候,发现文件的分布和文件夹的名字是极其不合理的,所以移动和重命名了某些文件. 在删除(git rm –r folder)一个空文件夹的时候,出现错误:fat ...
- 微软官网下载windows系统有点全
第一步:访问:https://www.microsoft.com/zh-cn/software-download/windows10ISO/ 默认就只能下载win10,这怎么行呢.巨硬程序员貌似没做服 ...
- 微信授权步骤与详解 -- c#篇
微信授权步骤与详解 -- c#篇 注:这里不涉及界面操作,只介绍代码操作. 1.基本原理如下: 从图上所知,第一步用户访问我们的网页,第二步我们后台跳转到微信授权页面,第三步用户点击授权,第四步微信重 ...
- Android 使用Font Awesome 显示文字图标
Android 使用Font Awesome 显示文字图标 简单几步就可以完成 简单的效果图: 1. 创建 assets 文件夹 在Android Studio 上的创建步骤为: 在 src/main ...
- python2与python3在windows下共存
python有python2(工业版)和python3,有时候我们会希望电脑上既有python2也有python3,!假设我们已经安装好,python2和python3了, 接下来我们找到python ...
- 安卓中的数据存储方式以及ContentProvider的简单介绍
1.介绍android的数据存储方式 File存储 sharedPrefrence存储方式 conmtentprovider sqlitedatabase 网络存储 2.请介绍下ContentPr ...
- KendoUI系列:MultiSelect
1.基本使用 1>.创建Input <link href="@Url.Content("~/Content/kendo/2014.1.318/kendo.common. ...