算法-无向图(连通分量,是否有环和二分图)
前面的文章实现了无向图深度优先搜索和广度优先搜索解决了无向图中的路径寻找,不过无向图中还有几个比较常见的问题需要解决,判断图中的连通分量,在无向图中,如果从顶点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]; |
作者:FlyElephant
出处:http://www.cnblogs.com/xiaofeixiang
说明:博客经个人辛苦努力所得,如有转载会特别申明,博客不求技惊四座,但求与有缘人分享个人学习知识,生活学习提高之用,博客所有权归本人和博客园所有,如有转载请在显著位置给出博文链接和作者姓名,否则本人将付诸法律。
出处:http://www.cnblogs.com/xiaofeixiang
说明:博客经个人辛苦努力所得,如有转载会特别申明,博客不求技惊四座,但求与有缘人分享个人学习知识,生活学习提高之用,博客所有权归本人和博客园所有,如有转载请在显著位置给出博文链接和作者姓名,否则本人将付诸法律。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述
2014-08-04 Http协议中Get和Post的浅谈