算法-无向图(连通分量,是否有环和二分图)

简介:

前面的文章实现了无向图深度优先搜索和广度优先搜索解决了无向图中的路径寻找,不过无向图中还有几个比较常见的问题需要解决,判断图中的连通分量,在无向图中,如果从顶点vi到顶点vj有路径,则称vi和vj连通。如果图中任意两个顶点之间都连通,则称该图为连通图,否则,称该图为非连通图,则其中的极大连通子图称为连通分量,这里所谓的极大是指子图中包含的顶点个数极大。

连通分量

为了编程和理解,我们还是使用之前文章的非连通图,如下图所示,图中有三个连通分量,看着这个图对照上文中的概念比较好理解:

 

代码中跟深度优先搜索有所改动,需要一个新的数组存储对应的值,数组中0-6都属于一个连通分量,那么我们可以将数组中索引在0-6之间的变量赋值为0,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@ interface  GraphCC : NSObject
 
//记录顶点是否被标记
@property  (strong,nonatomic)  NSMutableArray  *marked;
 
@property (assign,nonatomic)  NSInteger count; //连通的分量
 
@property  (strong,nonatomic)  NSMutableArray  *ids; //顶点所在的连通分量的标识符
 
//连通分量递归初始化
-(instancetype)initWithGraph:(Graph *)graph;
 
-( void )depthSearch:(Graph *)graph  vertex:(NSInteger)vertex;
 
 
@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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@implementation GraphCC
 
#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:(Graph *)graph{
     self=[super init];
     if  (self) {
         for  (NSInteger i=0; i<graph.vertexs;i++) {
             [self.marked addObject:[NSNull  null ]];
             [self.ids addObject:[NSNull  null ]];
         }
         //遍历图的顶点
         for  (NSInteger j=0; j<graph.vertexs; j++) {
             if  (![self isMarked:j]) {
                 [self depthSearch:graph vertex:j];
                 self.count++;
             }
         }
     }
     return  self;
}
//博客园-FlyElephant:http://www.cnblogs.com/xiaofeixiang/
-( void )depthSearch:(Graph *)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];
}
 
@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
Graph  *graph=[[Graph alloc]initWithVertex:13];
[graph addEdges:0 endVertex:1];
[graph addEdges:0 endVertex:2];
[graph addEdges:0 endVertex:5];
[graph addEdges:0 endVertex:6];
[graph addEdges:3 endVertex:4];
[graph addEdges:3 endVertex:5];
[graph addEdges:4 endVertex:5];
[graph addEdges:4 endVertex:6];
[graph addEdges:7 endVertex:8];
[graph addEdges:9 endVertex:10];
[graph addEdges:9 endVertex:11];
[graph addEdges:9 endVertex:12];
[graph addEdges:11 endVertex:12];
GraphCC  *graphCC=[[GraphCC 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: @"--" ]);
}

效果如下:

是否有环

环简单就是几个顶点之间是否存在闭合,从顶点1通过若干个顶点是否可以返回到顶点1,此次判断通过之前文章的一个连通图:

1
2
3
4
5
6
7
8
9
10
11
12
@ interface  GraphCycle : NSObject
 
//记录顶点是否被标记
@property  (strong,nonatomic)  NSMutableArray  *marked;
 
@property (assign,nonatomic)  Boolean hasCycle;
//初始化
-(instancetype)initWithGraph:(Graph *)graph;
 
-( void )depthSearch:(Graph *)graph  vertex:(NSInteger)vertex nextVertex:(NSInteger)nextVertex;
 
@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
39
40
41
42
43
@implementation GraphCycle
#pragma mark  getter and setter
-(NSMutableArray *)marked{
     if  (!_marked) {
         _marked=[[NSMutableArray alloc]initWithCapacity:1];
     }
     return  _marked;
}
 
 
-(instancetype)initWithGraph:(Graph *)graph{
     self=[super init];
     if  (self) {
         for  (NSInteger i=0; i<graph.vertexs;i++) {
             [self.marked addObject:[NSNull  null ]];
         }
         //遍历图的顶点
         for  (NSInteger s=0; s<graph.vertexs; s++) {
             if  (![self isMarked:s]) {
                 [self depthSearch:graph vertex:s nextVertex:s];
             }
         }
     }
     return  self;
}
//http://www.cnblogs.com/xiaofeixiang/
-( void )depthSearch:(Graph *)graph vertex:(NSInteger)vertex nextVertex:(NSInteger)nextVertex{
     self.marked[vertex]=[NSNumber numberWithBool: true ];
     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 nextVertex:vertex];
         }
         //最开始检测到的环是0-1-2
         else  if  (temp!=nextVertex) self.hasCycle= true ;
     }
}
 
-(Boolean)isMarked:(NSInteger)vertex{
     return  self.marked[vertex]==[NSNull  null ]? false :[self.marked[vertex] boolValue];
}
 
@end

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Graph  *graph=[[Graph alloc]initWithVertex:6];
[graph addEdges:0 endVertex:5];
[graph addEdges:2 endVertex:4];
[graph addEdges:2 endVertex:3];
[graph addEdges:1 endVertex:2];
[graph addEdges:0 endVertex:1];
[graph addEdges:3 endVertex:4];
[graph addEdges:5 endVertex:3];
[graph addEdges:0 endVertex:2];
GraphCycle *cycle=[[GraphCycle alloc]initWithGraph:graph];
if  ([cycle hasCycle]) {
     NSLog( @"图中含有环" );
} else {
     NSLog( @"图中不含有环" );
}
NSLog( @"技术交流群:%@" , @"228407086" );
NSLog( @"原文地址:http://www.cnblogs.com/xiaofeixiang" );

测试效果:

二分图

我们也许会看到过这么一个问题就是是否能够用两种颜色将图中的所有顶点着色,,使得任意一条边的两个点都不相同,其实这个问题等价于图是否是二分图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ interface  GraphTwoColor : NSObject
 
//标记数组
@property  (strong,nonatomic)  NSMutableArray  *marked;
 
@property  (strong,nonatomic)  NSMutableArray  *color;
 
@property (assign,nonatomic)  Boolean isTwoColorable;
//初始化
-(instancetype)initWithGraph:(Graph *)graph;
//深度搜索
-( void )depthSearch:(Graph *)graph  vertex:(NSInteger)vertex;
 
 
@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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@implementation GraphTwoColor
#pragma mark  getter and setter
-(NSMutableArray *)marked{
     if  (!_marked) {
         _marked=[[NSMutableArray alloc]initWithCapacity:1];
     }
     return  _marked;
}
 
-(NSMutableArray *)color{
     if  (!_color) {
         _color=[[NSMutableArray alloc]initWithCapacity:1];
     }
     return  _color;
}
 
 
-(instancetype)initWithGraph:(Graph *)graph{
     self=[super init];
     if  (self) {
         for  (NSInteger i=0; i<graph.vertexs;i++) {
             [self.marked addObject:[NSNull  null ]];
             [self.color addObject:[NSNull  null ]];
         }
         self.isTwoColorable= true ;
         //遍历图的顶点
         for  (NSInteger s=0; s<graph.vertexs; s++) {
             if  (![self isMarked:s]) {
                 [self depthSearch:graph vertex:s];
             }
         }
     }
     return  self;
}
//http://www.cnblogs.com/xiaofeixiang/
-( void )depthSearch:(Graph *)graph vertex:(NSInteger)vertex{
     self.marked[vertex]=[NSNumber numberWithBool: true ];
     
     for  (NSInteger i=0; i<[graph.adjDataSource[vertex] count]; i++) {
         NSInteger temp=[[graph.adjDataSource[vertex] objectAtIndex:i] integerValue];
         if  (![self isMarked:temp]) {
             //二分图两个相邻的节点颜色不一样
             self.color[temp]=[NSNumber numberWithBool:![self isColor:vertex]];
             [self depthSearch:graph vertex:temp];
         }
         //相邻的节点颜色相同则不是二分图
         else  if  ([self isColor:temp]==[self isColor:vertex]) self.isTwoColorable= false ;
     }
}
 
-(Boolean)isMarked:(NSInteger)vertex{
     return  self.marked[vertex]==[NSNull  null ]? false :[self.marked[vertex] boolValue];
}
 
-(Boolean)isColor:(NSInteger)vertex{
     return  self.color[vertex]==[NSNull  null ]? false :[self.color[vertex] boolValue];
}
 
@end

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Graph  *graph=[[Graph alloc]initWithVertex:6];
[graph addEdges:0 endVertex:5];
[graph addEdges:2 endVertex:4];
[graph addEdges:2 endVertex:3];
[graph addEdges:1 endVertex:2];
[graph addEdges:0 endVertex:1];
[graph addEdges:3 endVertex:4];
[graph addEdges:5 endVertex:3];
[graph addEdges:0 endVertex:2];
GraphTwoColor  *graphColor=[[GraphTwoColor alloc]initWithGraph:graph];
if  ([graphColor isTwoColorable]) {
     NSLog( @"图是一个二分图" );
} else {
     NSLog( @"图不是一个二分图" );
}
NSLog( @"技术交流群:%@" , @"228407086" );
NSLog( @"原文地址:http://www.cnblogs.com/xiaofeixiang" );

测试效果:

如果我们修改一下Graph,比如图只有四个顶点,四条边,肯定是一个二分图:

1
2
3
4
5
Graph  *graph=[[Graph alloc]initWithVertex:4];
[graph addEdges:0 endVertex:1];
[graph addEdges:0 endVertex:2];
[graph addEdges:1 endVertex:3];
[graph addEdges:2 endVertex:3];
本文转自Fly_Elephant博客园博客,原文链接:http://www.cnblogs.com/xiaofeixiang/p/4700958.html,如需转载请自行联系原作者
相关文章
|
2天前
|
算法 数据挖掘 定位技术
【MATLAB】赫尔默特方差分量估计算法
【MATLAB】赫尔默特方差分量估计算法
34 0
|
2天前
|
算法 数据挖掘 定位技术
【MATLAB】赫尔默特方差分量估计算法
【MATLAB】赫尔默特方差分量估计算法
60 0
|
7月前
|
存储 算法 图计算
TuGraph Analytics图计算快速上手之弱联通分量算法
TuGraph Analytics是蚂蚁集团近期开源的分布式流式图计算,目前广泛应用在蚂蚁集团的金融、社交、风控等诸多领域。
|
10月前
|
算法
使用3种不同的算法从倾斜风速计中检索3个风分量(Matlab代码实现)
使用3种不同的算法从倾斜风速计中检索3个风分量(Matlab代码实现)
|
10月前
|
安全 算法 Go
有向图的强联通分量(SCC)Tarjan算法
有向图的强联通分量(SCC)Tarjan算法
125 0
|
11月前
|
存储 人工智能 算法
LeetCode算法小抄 -- 经典图论算法 之 二分图
LeetCode算法小抄 -- 经典图论算法 之 二分图
|
算法
设计算法求无向图的深度优先生成树
设计算法求无向图的深度优先生成树
69 0
|
机器学习/深度学习 算法
无向图的算法:Kruskal算法与Prim算法生成最小生成树
无向图的算法:Kruskal算法与Prim算法生成最小生成树
154 0
|
算法 索引
【切图仔的算法修炼之旅】LeetCode141:判断链表是否有环
【切图仔的算法修炼之旅】LeetCode141:判断链表是否有环
77 0
【切图仔的算法修炼之旅】LeetCode141:判断链表是否有环
|
算法
【牛客刷题-算法】NC4 判断链表中是否有环
【牛客刷题-算法】NC4 判断链表中是否有环
85 0
【牛客刷题-算法】NC4 判断链表中是否有环