算法-强连通分量和Kosaraju算法

简介:

有向图中,连通性比较好理解,如果两个顶点V和顶点W是可达的,可以称之为强连通的,即存在路径A→B,同时也存在一条有向路径B→A.从之前的有向环的判定过程中其实我们可以得到一个结论就是两个是强连通的当且仅当它们都在一个普通的有向环中。强连通将所有的顶点分为了不同的集合,每个集合都是由相互均为强连通性的顶点的最大子集组成的,我们将这些集合称之为强连通分量。

基础概念

一般来说技术服务于生活,如果将我们看到网页作为顶点,页面指向另外一个页面的超链接作为边,可以将数量庞大的网页分为不同的大小进行处理,作为软件工程师或者说码农,经常遇到的就是模块的封装,如果将模块作为顶点,模块之间的引用作为边,通过强连通图我们可以更好进行模块之间的调用关系考虑适时的解耦。如果通过平方级别的算法解决强连通分量,那么遇到大型有向图的过程我们就会有点力不从心。Kosaraju的算法(也称为Kosaraju-Sharir算法)是线性时间的算法来解决有向图中的连通性查询以及处理强连通分量的数量。

采用之前有向环中的图片:

API定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@ interface  KosarajuCC : NSObject
 
//记录顶点是否被标记
@property  (strong,nonatomic)  NSMutableArray  *marked;
 
@property (assign,nonatomic)  NSInteger count; //连通的分量
 
@property  (strong,nonatomic)  NSMutableArray  *ids; //顶点所在的连通分量的标识符
 
//连通分量递归初始化
-(instancetype)initWithGraph:(Digraph *)graph;
 
-( void )depthSearch:(Digraph *)graph  vertex:(NSInteger)vertex;
//判断两个顶点之间是否存在连通性
-(BOOL)stronglyConnected:(NSInteger)vertex  otherVertex:(NSInteger)otherVertex;
 
@end

算法实战

通过API的定义,如果对比之前之前无向图中的API,我们发现基本上没有变化,具体实现的过程中变化也很小,需要之前基于深度优先搜索的顶点排序,取出逆后序集合进行遍历即可,之后和无向图中一样进行递归判断存储在数组中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@implementation KosarajuCC
 
#pragma mark  getter and setter
-(NSMutableArray *)marked{
     if  (!_marked) {
         _marked=[[NSMutableArray alloc]initWithCapacity:1];
     }
     return  _marked;
}
 
-(NSMutableArray *)ids{
     if  (!_ids) {
         _ids=[[NSMutableArray alloc]initWithCapacity:1];
     }
     return  _ids;
}
 
-(instancetype)initWithGraph:(Digraph *)graph{
     self=[super init];
     if  (self) {
         for  (NSInteger i=0; i<graph.vertexs;i++) {
             [self.marked addObject:[NSNull  null ]];
             [self.ids addObject:[NSNull  null ]];
         }
         DepthFirstOrder *order=[[DepthFirstOrder alloc]initWithGraph:[graph reverse]];
         //遍历图的顶点
         for  (NSInteger j=0; j<[order.reversePostStack count]; j++) {
             NSInteger  temp=[[order.reversePostStack objectAtIndex:j] integerValue];
             if  (![self isMarked:temp]) {
                 [self depthSearch:graph vertex:temp];
                 self.count++;
             }
         }
     }
     return  self;
}
//博客园-FlyElephant:http://www.cnblogs.com/xiaofeixiang/
-( void )depthSearch:(Digraph *)graph vertex:(NSInteger)vertex{
     self.marked[vertex]=[NSNumber numberWithBool: true ];
     //同一分量中顶点的赋值
     self.ids[vertex]=[NSNumber numberWithInteger:self.count];
     for  (NSInteger i=0; i<[graph.adjDataSource[vertex] count]; i++) {
         NSInteger temp=[[graph.adjDataSource[vertex] objectAtIndex:i] integerValue];
         if  (![self isMarked:temp]) {
             [self depthSearch:graph vertex:temp];
         }
     }
}
 
-(Boolean)isMarked:(NSInteger)vertex{
     return  self.marked[vertex]==[NSNull  null ]? false :[self.marked[vertex] boolValue];
}
 
-(BOOL)stronglyConnected:(NSInteger)vertex  otherVertex:(NSInteger)otherVertex{
     return  [self.ids[vertex] integerValue]==[self.ids[otherVertex] integerValue];
}
@end

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Digraph  *graph=[[Digraph alloc]initWithVertex:13];
[graph addEdges:4 endVertex:2];
[graph addEdges:2 endVertex:3];
[graph addEdges:3 endVertex:2];
[graph addEdges:6 endVertex:0];
[graph addEdges:0 endVertex:1];
[graph addEdges:2 endVertex:0];
[graph addEdges:11 endVertex:12];
[graph addEdges:12 endVertex:9];
[graph addEdges:9 endVertex:10];
[graph addEdges:9 endVertex:11];
[graph addEdges:8 endVertex:9];
[graph addEdges:10 endVertex:12];
[graph addEdges:11 endVertex:4];
[graph addEdges:4 endVertex:3];
[graph addEdges:3 endVertex:5];
[graph addEdges:7 endVertex:8];
[graph addEdges:8 endVertex:7];
[graph addEdges:5 endVertex:4];
[graph addEdges:0 endVertex:5];
[graph addEdges:6 endVertex:4];
[graph addEdges:6 endVertex:9];
[graph addEdges:7 endVertex:6];
KosarajuCC  *graphCC=[[KosarajuCC alloc]initWithGraph:graph];
for  (NSInteger i=0; i<graphCC.count; i++) {
     NSMutableArray  *dataSource=[[NSMutableArray alloc]initWithCapacity:1];
     for  (NSInteger j=0; j<graph.vertexs; j++) {
         if  ([graphCC.ids[j] integerValue]==i) {
             [dataSource addObject:[NSNumber numberWithInteger:j]];
         }
     }
     NSLog( @"分量%ld:%@" ,i,[dataSource componentsJoinedByString: @"--" ]);
}
NSInteger  vertex=0,otherVertex=1;
Boolean  cc=[graphCC stronglyConnected:vertex otherVertex:otherVertex];
NSLog( @"节点%ld和节点%ld %@强连通的" ,vertex,otherVertex,cc== true ? @"是" : @"不是" );
NSLog( @"技术交流群:%@" , @"228407086" );
NSLog( @"博客园-FlyElephant:http://www.cnblogs.com/xiaofeixiang" );

测试结果:

本文转自Fly_Elephant博客园博客,原文链接:http://www.cnblogs.com/xiaofeixiang/p/4715319.html,如需转载请自行联系原作者

相关文章
|
算法
图论——强连通分量:Tarjan算法。
在有向图G中,如果两个定点u,v间存在一条u到v的路径,也存在一条v到u的路径,则称u,v是强连通的。 若有向图G的任意两点都强联通,则称G是一个强联通图。 非强连通图的极大强连通子图称为强连通分量。   这里,极大强连通子图可以理解为一个子图是强连通图,且它的任意子图都不是强联通。
2651 0
|
算法 C++ BI
图论——强连通分量:Tarjan算法——练习1
上一次我们详细介绍了强连通分量的Tarjan算法,今天呢,我们来做一些习题来巩固Tarjan算法,毕竟它十分重要。 Tarjan算法详解 上面是上一次的详解,在做题时可供参考。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                               练习一般采用洛谷题库。
1313 0
|
存储 算法
算法学习之路|强连通分量+缩点
路漫漫其修远兮,算法学习
1727 0
|
算法
poj2186Popular Cows(Kosaraju算法--有向图的强连通分量的分解)
1 /* 2 题目大意:有N个cows, M个关系 3 a->b 表示 a认为b popular;如果还有b->c, 那么就会有a->c 4 问最终有多少个cows被其他所有cows认为是popular! 5 6 思路:强连通分量中每...
1027 0
|
2天前
|
机器学习/深度学习 算法
基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真
本项目基于改进遗传优化的BP神经网络进行金融序列预测,使用MATLAB2022A实现。通过对比BP神经网络、遗传优化BP神经网络及改进遗传优化BP神经网络,展示了三者的误差和预测曲线差异。核心程序结合遗传算法(GA)与BP神经网络,利用GA优化BP网络的初始权重和阈值,提高预测精度。GA通过选择、交叉、变异操作迭代优化,防止局部收敛,增强模型对金融市场复杂性和不确定性的适应能力。
103 80
|
21天前
|
算法
基于WOA算法的SVDD参数寻优matlab仿真
该程序利用鲸鱼优化算法(WOA)对支持向量数据描述(SVDD)模型的参数进行优化,以提高数据分类的准确性。通过MATLAB2022A实现,展示了不同信噪比(SNR)下模型的分类误差。WOA通过模拟鲸鱼捕食行为,动态调整SVDD参数,如惩罚因子C和核函数参数γ,以寻找最优参数组合,增强模型的鲁棒性和泛化能力。
|
7天前
|
供应链 算法 调度
排队算法的matlab仿真,带GUI界面
该程序使用MATLAB 2022A版本实现排队算法的仿真,并带有GUI界面。程序支持单队列单服务台、单队列多服务台和多队列多服务台三种排队方式。核心函数`func_mms2`通过模拟到达时间和服务时间,计算阻塞率和利用率。排队论研究系统中顾客和服务台的交互行为,广泛应用于通信网络、生产调度和服务行业等领域,旨在优化系统性能,减少等待时间,提高资源利用率。