强连通:在有向图G中,两个顶点间至少存在一条路径,则两个点强连通。

强连通图:在有向图中,每两个顶点都强连通,则有向图G就是一个强连通图。

强连通分量:在非强连通图中的极大强连通子图,就称为强连通分量。

直接根据定义,可以通过双向遍历取交集的方法求强连通分量,但是其复杂度为O(N^2+M)。更好的方法是用tarjan算法,其时间复杂度为O(N+M)。

tarjan:其实就是对图的深度优先遍历。

算法模拟:

定义 DFN [u]为节点u被搜索到时的次序编号(也就是所遍历的第几个);

定义LOW[U]为U或者U 的子数能够追溯到最早的栈中节点的次序号。

  (当DFN[U] ==  LOE[U] 时,以U为根的搜索子树上所有节点是一个强连通分量。实质上就是这个点的整个子树回溯完了,没有链接的路了)

此时的F点DNF[F]==LOW[F]=4,所以F为一个强连通分量,只有它自己本身。

然后返回节点E,此时发现 DFN[E] == LOW[E] =5,所以E也单独为一个强连通分量,只有他本身。

然后返回节点C:

从C节点继续搜其他的C的子树,搜到节点D,将D加入栈中。然后D有  D ->  F ,但是F 已经出栈,所以F节点直接跳过,

还有一个节点A , 但是呢,A节点已经在栈中,所以LOW[D] = DFN[A] = 1,然后是一个回溯的过程,所以呢,LOW[C] = LOW [D] =1;

所以说明这三个节点已经是一个强连通分量了。

继续回到节点A,最后访问B节点,,然后 B-> D ,然而 D点还在栈中,所以LOW[B] = DFN [ D ] = 5 ,DFN[B] = 6 ;

没有其他点了,算法结束,最后得到 {A,B,C,D} ,{E}, {F}为强连通分量。

在tarjan算法中,每个点只被访问了一次,而且只进行了一次的栈,每条边也只被访问了一次,。所以该算法的时间复杂度为O(N+M);

程序模板:

 #include <cstdio>
#include <cstring>
#include <iostream>
using namespace std; #define N 100
#define M 100 struct node
{
int v;
int next;
}; node edge[M];//边的集合 int a[N];//顶点的集合
int instack[N];//模拟栈,标记是否在栈中
int stack[N];
int belong[N];//各个顶点属于哪个强连通分量
int DFN[N];
int LOW[N];//栈中能够追溯到最早的节点序号
int n;//点的个数
int m;//边的个数
int count;//边的计数器
int index;//序号(时间戳)
int top;
int sun;//强连通分量的个数 //用邻接表存储
void add_edge(int u,int v)
{
edge[count].next=a[u];
edge[count].v=v;
a[u]=count++;
} void tarjan(int u)
{
int i,j;
int v;
DFN[u]=LOW[u]=++index;
instack[u]=true;
stack[++top]=u;//进栈
for(i=a[u];i!=-;i=edge[i].next)
{
v=edge[i].v;
if(!DFN[v])//如果DFN[v]为0,没被访问
{
tarjan(v);
//一个回溯的过程
if(LOW[v]<LOW[u])
LOW[u]=LOW[v];
}
else
{
if(instack[v]&&DFN[v]<LOW[u])
LOW[u]=DFN[v];
}
}
if(DFN[u]==LOW[u])
{
sun++;
//出栈
do
{
j=stack[top--];
instack[j]=false;
belong[j]=sun;
}while(j!=u); }
} void solve()
{
int i;
top=index=sun=;
memset(DFN,,sizeof(DFN));
memset(LOW,,sizeof(LOW));
for(i=;i<=n;i++)
{
if(!DFN[i])
tarjan(i);
}
} int main()
{
int i,j,k;
count=;
memset(a,-,sizeof(a));
scanf("%d %d",&n,&m);
for(i=;i<=m;i++)
{
scanf("%d %d",&j,&k);
add_edge(j,k);
}
solve();
for(i=;i<=n;i++)
printf("%d ",belong[i]);
}

最新文章

  1. 简单配置和使用Maven
  2. 纯CSS打造银色MacBook Air(完整版)
  3. swift 判断输入的字符串是否为数字
  4. CSRF(Cross-site request forgery)跨站请求伪造
  5. [转]ubuntu(12.04)下, 命令 ,内核 源代码的获取
  6. Python开发【第二十二篇】:Web框架之Django【进阶】
  7. DisplayMetircs 类
  8. (转)SWT的CheckBoxTreeViewer的相关用法
  9. shiro中 UnknownAccountException
  10. BZOJ2004: [Hnoi2010]Bus 公交线路
  11. ios学习- 10大iOS开发者最喜爱的类库
  12. E. Neko and Flashback
  13. 重磅|0元学 Python运维开发,别再错过了
  14. 数据导出之winfrom导出word(二)
  15. android应用搬家的实现
  16. 单点登录(三)-----实战-----cas server 源码下载和部署
  17. 微信小程序裁剪图片成圆形
  18. Linux TCP不同状态的连接数统计
  19. Learning How to Learn学习笔记(转)
  20. Jsp页面中的中文乱码问题解决

热门文章

  1. label 对齐
  2. Python开发【2.1 面向对象】
  3. Snail—iOS网络学习之得到网络上的数据
  4. MyCAT简单入门配置
  5. 如何查看Java进程并获取进程ID?
  6. 两篇C++和VC++字符串的文章
  7. Java代理(Aop实现的原理)
  8. ActiveMQ 入门使用p2p模型-主动消费
  9. js中的关系操作符
  10. php基础篇:echo 与 print 的区别