算法-有向图及可达性
图是由顶点和边连接而成,如果边是没有方向的是就是之前文章中说的无向图,关于无向图可以参考本人之前的文章,如果边是有方向的,则称之为有向图。从顶点A→B,我们可以理解为A到B可达,有向图和无向图一样通过邻接表保存每一条边,由于边是有方向的,因此在添加边的过程中只需要添加一条边即可。关于可达性一个节点数组的可达性,采用的方法是之前的深度优先搜索一样的代码,通过递归将标记位Bool标记位判断数组中每个顶点的可达性。为了测试,选择下面一张有向图:
有向图基础
通过图片我们可以发现图中有13个节点,22条边,顶点0指出的节点有1,5,指入的节点有2,6,我们先实现所有顶点指出的节点,之后可以通过反转判断所有节点的指入节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @ interface Digraph : NSObject //顶点的总数 @property (assign,nonatomic) NSInteger vertexs; //边的数总数 @property (assign,nonatomic) NSInteger edges; //连接点的边 @property (strong,nonatomic) NSMutableArray *adjDataSource; -(instancetype)initWithVertex:(NSInteger)vertexs; //添加一条有向边 startVertex→endVertex -( void )addEdges:(NSInteger)startVertex endVertex:(NSInteger)endVertex; -(Digraph *)reverse; //该图的反向图 @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 | @implementation Digraph -(instancetype)initWithVertex:(NSInteger)vertexs{ self=[super init]; if (self) { self.vertexs=vertexs; for (NSInteger i=0; i<vertexs; i++) { NSMutableArray *neighbourVertex=[[NSMutableArray alloc]initWithCapacity:1]; [self.adjDataSource addObject:neighbourVertex]; //创建邻接表,将所有链表初始化为空 } } return self; } //http://www.cnblogs.com/xiaofeixiang -( void )addEdges:(NSInteger)startVertex endVertex:(NSInteger)endVertex{ //将endVertex添加到startVertex的链表中 [self.adjDataSource[startVertex] insertObject:[NSNumber numberWithInteger:endVertex] atIndex:0]; self.edges=self.edges+1; } -(Digraph *)reverse{ Digraph *digraph=[[Digraph alloc]initWithVertex:self.vertexs]; for (NSInteger i=0; i<self.vertexs; i++) { NSMutableArray *tempArr=self.adjDataSource[i]; for (NSInteger j=0; j<[tempArr count]; j++) { [digraph addEdges:[tempArr[j] integerValue] endVertex:i]; } } return digraph; } #pragma mark getter and setter -(NSMutableArray *)adjDataSource{ if (!_adjDataSource) { _adjDataSource=[[NSMutableArray alloc]initWithCapacity:1]; } return _adjDataSource; } @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 | 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]; for (NSInteger i=0; i<[graph.adjDataSource count]; i++) { NSLog( @"节点%ld指出→的节点:%@" ,i,[graph.adjDataSource[i] componentsJoinedByString: @"--" ]); } NSLog( @"技术交流群:%@" , @"228407086" ); NSLog( @"原文地址:http://www.cnblogs.com/xiaofeixiang" ); |
测试效果:
现在可以判断出顶点的指出节点,实现文件中有一个reverse方法将图反转,求出顶点的转入节点:
1 2 3 4 5 6 | Digraph *digraph=[graph reverse]; for (NSInteger i=0; i<[digraph.adjDataSource count]; i++) { NSLog( @"指入%ld⬅️的节点:%@" ,i,[digraph.adjDataSource[i] componentsJoinedByString: @"--" ]); } NSLog( @"技术交流群:%@" , @"228407086" ); NSLog( @"原文地址:http://www.cnblogs.com/xiaofeixiang" ); |
测试效果:
可达性
可达性的判断和之前的深度优先搜索基本没变化,先来看一下需要实现的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @ interface DirectedDFS : NSObject //标记数组 @property (strong,nonatomic) NSMutableArray *marked; //找到arr中顶点可达的所有顶点 -(instancetype)initDirectedDFSWithVertex:(Digraph *)graph vertexArr:(NSArray *)arr; //在graph中找到从vertex可达的所有顶点 -( void )directedDFS:(Digraph *)graph vertex:(NSInteger)vertex; //vertex是否可达 -(Boolean)isMarked:(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 | @implementation DirectedDFS #pragma mark getter and setter -(NSMutableArray *)marked{ if (!_marked) { _marked=[[NSMutableArray alloc]initWithCapacity:1]; } return _marked; } -(instancetype)initDirectedDFSWithVertex:(Digraph *)graph vertexArr:(NSArray *)arr{ self=[super init]; if (self) { for (NSInteger i=0; i<graph.vertexs;i++) { [self.marked addObject:[NSNull null ]]; } //遍历有向图中的顶点 for (NSInteger j=0; j<[arr count]; j++) { if (![self isMarked:[arr[j] integerValue]]) { [self directedDFS:graph vertex:[arr[j] integerValue]]; } } } return self; } //博客园-FlyElephant:http://www.cnblogs.com/xiaofeixiang/ -( void )directedDFS:(Digraph *)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 directedDFS: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 | NSArray *sources=[NSArray arrayWithObjects: @"2" , nil]; DirectedDFS *directedDFS=[[DirectedDFS alloc]initDirectedDFSWithVertex:graph vertexArr:sources]; NSMutableArray *reachableArr=[[NSMutableArray alloc]initWithCapacity:1]; for (NSInteger i=0; i<graph.vertexs; i++) { if (directedDFS.marked[i]&&directedDFS.marked[i]!=[NSNull null ]) { [reachableArr addObject:[NSNumber numberWithInteger:i]]; } } NSLog( @"可达的节点:%@" ,[reachableArr componentsJoinedByString: @"--" ]); NSLog( @"技术交流群:%@" , @"228407086" ); NSLog( @"博客园-FlyElephant:http://www.cnblogs.com/xiaofeixiang" ); |
测试效果:
作者:FlyElephant
出处:http://www.cnblogs.com/xiaofeixiang
说明:博客经个人辛苦努力所得,如有转载会特别申明,博客不求技惊四座,但求与有缘人分享个人学习知识,生活学习提高之用,博客所有权归本人和博客园所有,如有转载请在显著位置给出博文链接和作者姓名,否则本人将付诸法律。
出处:http://www.cnblogs.com/xiaofeixiang
说明:博客经个人辛苦努力所得,如有转载会特别申明,博客不求技惊四座,但求与有缘人分享个人学习知识,生活学习提高之用,博客所有权归本人和博客园所有,如有转载请在显著位置给出博文链接和作者姓名,否则本人将付诸法律。
标签:
算法
, Objective-C
【推荐】国内首个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的设计模式综述