有向图的强连通分量

利用深度优先搜索,求有向图G的强连通分支的算法步骤:  
  1)对G进行深度优先搜索并按递归调用完成的先后顺序对各顶点编号;  
  2)改变G的每条边的方向,构造出新的有向图Gr;  
  3)按1)中的确定的顶点编号,从编号最大的顶点开始对Gr进行深度优先搜索。如果搜索的过程中没有访问遍Gr的所有顶点,则从未被访问过的顶点中选取编号最大的顶点,并从此顶点开始继续做深度优先搜索;  
  4)在最后得到的Gr的深度优先生成森林中,每棵树上的顶点组成G的一个强连通分支。   
   

(一)问题如果一个有向图中,取某一组顶点,满足:(1)其中任意甲乙两点间,一定既可找到甲至乙的有向路径,又可找到乙至甲的有向路径;(2)再向该组点增加其他任意一点,就不能保持任意两点间的双向连通性;则称这一组点构成该有向图的一个强连通分支。一般而言,一个有向图可由多个强连通分支组成。例如,图1的有向图则由三个强连通分支:{v0,v1,v2},{v3,v4},{v5}组成。请编一程序,对任一有向图,求解出其每一个强连通分支。

(二)分析与算法设计首先,给出一个结论:结论1: 强连通分支的点集是对原有向图顶点集的一个划分。即指:任意两个强连通分支没有共同的点;任意一点一定属于某一强连通分支。2001年第6期我们已经讨论了强连通图的问题,可引出结论: 存在一个回路穿过的最大点集构成一个强连通分支。第6期已经给出了相应搜寻回路的函数程序,同时指出了编程的一个重要细节问题是对局部回路无限死循环的避免,在此基础上,利用递归函数穷尽所有回路,并将所有回路的点集都归入一个强连通分支即可;最后依次处理完每一个未归并点则完成任务。这里给出另一个算法设计,该算法每一轮采用走过的边不再走的简单方法避免陷入局部死循环,再采用多轮递归完成对所有强连通分支点的穷尽:(0)首先,初始化,置所有点为未标定强连通分支序号的点,得未标定点集A,取当前强连通标定序号为imk=1;(1)任取一未标定点标记为imk类点;(2)构成本轮第imk类强连通分支标定点集Bimk;(3)对imk标定点集Bimk中的每一个点,在有向图中进行搜寻,如果找到一条至该标定点集某点的一条通路或回路,则将该通路或回路上的所有点标定为该标定集之点,重复这一步骤,至完成对所有标定集点的搜寻为止。(4)如未标定点集A非空,则imk=imk+1,转入(1);否则算法结束。

(三)源程序
//罗光宣 任琼英 VC6.0
//2001 年2 月调试通过
#include <stdio.h>
#include <stdlib.h>
int n,m,**pd; //n:有向图点数; m:处理过程中当前已处理点数; pd有向
图邻接矩阵
int *vmk,imk; //vmk[n]:点的归类类别; imk:当前处理的类别值
//该函数读有向图数据,并得到对应简单图的邻接矩阵
int rdata(char *fn)
{ FILE *fp;
int i,j,m,v1,v2;
if((fp=fopen(fn,"r"))==NULL)return(-1);
fscanf(fp,"%d %d",&n,&m);
if(n==0)return(-2);
if((pd=(int **)malloc(n*sizeof(int *)))==NULL)return(-3);
for(i=0;i<n;++i)if((pd[i]=(int *)malloc(n*sizeof(int)))==NULL)return
(-4);
for(i=0;i<n;++i)
for(j=0;j<n;++j)
pd[i][j]=0;
for(i=0;i<m;++i)
{ fscanf(fp,"%d %d",&v1,&v2);
if(v1!=v2 || pd[v1][v2]==0)pd[v1][v2]=1;
}
return(1);
}
//该函数寻找从第i0点出发,且回到第imk类点的通路或回路,如找到将
路径中点归入第imk类
//函数返回1值,找不到则返回-1值
int getpdc(int id)
{ int i;
if(m>n)return(-1);
for(i=0;i<n;++i)
if(i!=id && pd[id][i]!=0)
if(vmk[i]==imk)return(1);
else if(vmk[i]==0)
{ pd[id][i]=0;
vmk[i]=-imk;
++m;
if(getpdc(i)>0){vmk[i]=-vmk[i];return(1);}
else {pd[id][i]=1; vmk[i]=0; --m;}
}
return(-1);
}
void main(void)
{
int i,j,*vh;
if(rdata("input.dat")<0)//读有向图数据
{ printf("数据文件数据读入不正确!"); exit(1);}
vmk=(int *)malloc(n*sizeof(int));
vh =(int *)malloc(n*sizeof(int));
for(i=0;i<n;++i)
{ vmk[i]=0;
vh[i]=0;
}
imk=1;
m=1;
while(m<n)
{
lin1:
for(i=0;i<n;++i)//
if(vmk[i]==imk && vh[i]==0)
{vh[i]=1;
getpdc(i);
goto lin1;
}
if(m>=n)break;
++imk;
for(i=0;i<n;++i)
if(vmk[i]==0)
{vmk[i]=imk; ++m; break;}
}
printf("该有向图划分为%d个强连通分支!",imk);
for(i=1;i<=imk;++i)
for(j=0,printf("第%d个强连通分支含的点有:",i);j<n;++j)
if(vmk[j]==i)printf("%d ",j);
for(i=0;i<n;++i)free(pd[i]);
free(pd);free(vmk);free(vh);
}
(四)运行实例
实例1
如图2所示的有向图则input.dat输入文件为:
8 10
0 1
1 2
2 5
5 4
4 3
3 0
6 4
6 1
6 7
7 6
程序运行结果则为:
该有向图划分为2个强连通分支!
第1个强连通分支含的点有:0 1 2 3 4 5
第2个强连通分支含的点有:6 7
实例2
如图3所示的有向图则input.dat输入文件为:
7 8
0 1
1 2
2 5
5 4
4 3
3 0
4 6
6 1
程序运行结果则为:
该有向图划分为1个强连通分支!
第1个强连通分支含的点有:0 1 2 3 4 5 6
实例3
如图4所示的有向图则input.dat输入文件为:
7 8
0 1
1 2
2 5
5 4
4 3
3 0
6 4
1 6
程序运行结果则为:
该有向图划分为1个强连通分支!
第1 个强连通分支含的点有:0 1 2 3 4 5

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值