算法-无向图(深度优先搜索和广度优先搜索)
图中最常用到的两种搜索深度优先搜索和广度优先搜索,深度优先搜索是一种在开发爬虫早期使用较多的方法它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链接的Html文件) ,广度搜索属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
深度优先搜索
图中我们经常会遇到一个问题就是图的连通性,比如说从一个顶点到另外一个顶点,判断顶点和其他顶点之间的连通性,以下图为例:
搜索API定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @ interface DepthFirstSearch : NSObject //记录顶点是否被标记 @property (strong,nonatomic) NSMutableArray *marked; @property (assign,nonatomic) NSInteger count; //找到与七点vertex所有连通的节点 -(instancetype)initWithGraphAndVertex:(Graph *)graph vertex:(NSInteger)vertex; -( void )depthSearch:(Graph *)graph vertex:(NSInteger)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 | @implementation DepthFirstSearch #pragma mark getter and setter -(NSMutableArray *)marked{ if (!_marked) { _marked=[[NSMutableArray alloc]initWithCapacity:1]; } return _marked; } -(instancetype)initWithGraphAndVertex:(Graph *)graph vertex:(NSInteger)vertex{ self=[super init]; if (self) { for (NSInteger i=0; i<graph.vertexs;i++) { [self.marked addObject:[NSNull null ]]; } [self depthSearch:graph vertex:vertex]; } return self; } //http://www.cnblogs.com/xiaofeixiang/ -( void )depthSearch:(Graph *)graph vertex:(NSInteger)vertex{ self.marked[vertex]=[NSNumber numberWithBool: true ]; 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 | 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]; DepthFirstSearch *search=[[DepthFirstSearch alloc]initWithGraphAndVertex:graph vertex:0]; for (NSInteger i=0; i<graph.vertexs; i++) { NSLog( @"节点%ld--值为:%@" ,i,search.marked[i]); } NSLog( @"技术交流群:%@" , @"228407086" ); NSLog( @"原文地址:http://www.cnblogs.com/xiaofeixiang" ); |
测试效果:
1表示连通,节点0和其他节点直接都可以直接连通,我们通过递归的方式成功的判断亮点之间的连通性,顶点直接的连通性是通过边链接的,深度搜索将在此方法上改动然后给出单点之间的路径,注意是路径不是最短路径,我们会发现代码变动很小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //深度优先 @ interface DepthFirstPath : NSObject //记录顶点是否被标记 @property (strong,nonatomic) NSMutableArray *marked; //起点 @property (assign,nonatomic) NSInteger beginVertex; //从起点到一个顶点的已知路径上的最后一个顶点 @property (strong,nonatomic) NSMutableArray *edgeTo; @property (assign,nonatomic) NSInteger count; //找到与七点vertex所有连通的节点 -(instancetype)initWithGraphAndVertex:(Graph *)graph vertex:(NSInteger)vertex; -( void )depthSearch:(Graph *)graph vertex:(NSInteger)vertex; -(NSMutableArray *)pathTo:(NSInteger)vertex; //节点是否被标记 -(Boolean)hasPathTo:(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 DepthFirstPath #pragma mark getter and setter -(NSMutableArray *)marked{ if (!_marked) { _marked=[[NSMutableArray alloc]initWithCapacity:1]; } return _marked; } -(NSMutableArray *)edgeTo{ if (!_edgeTo) { _edgeTo=[[NSMutableArray alloc]initWithCapacity:1]; } return _edgeTo; } -(instancetype)initWithGraphAndVertex:(Graph *)graph vertex:(NSInteger)vertex{ self=[super init]; if (self) { for (NSInteger i=0; i<graph.vertexs;i++) { [self.marked addObject:[NSNull null ]]; [self.edgeTo addObject:[NSNull null ]]; } self.beginVertex=vertex; [self depthSearch:graph vertex:vertex]; } return self; } //http://www.cnblogs.com/xiaofeixiang -( void )depthSearch:(Graph *)graph vertex:(NSInteger)vertex{ self.marked[vertex]=[NSNumber numberWithBool: true ]; self.count++; for (NSInteger i=0; i<[graph.adjDataSource[vertex] count]; i++) { NSInteger temp=[[graph.adjDataSource[vertex] objectAtIndex:i] integerValue]; if (![self hasPathTo:temp]) { self.edgeTo[temp]=[NSNumber numberWithInteger:vertex]; [self depthSearch:graph vertex:temp]; } } } -(Boolean)hasPathTo:(NSInteger)vertex{ return self.marked[vertex]==[NSNull null ]? false :[self.marked[vertex] boolValue]; } -(NSMutableArray *)pathTo:(NSInteger)vertex{ if (![self hasPathTo:vertex]) { return NULL; } NSMutableArray *path=[[NSMutableArray alloc]initWithCapacity:1]; for (NSInteger i=vertex; i!=self.beginVertex; i=[self.edgeTo[i] integerValue]) { [path insertObject:[NSNumber numberWithInteger:i] atIndex:0]; } [path insertObject:[NSNumber numberWithInteger:self.beginVertex] atIndex:0]; return path; } @end |
代码中我们多了一个edgeTo数组记录当前索引顶点的最后一个顶点,当edgeTo[1]=2表示顶点1和顶点2之间是直接相连,最后输出路径的时候利用栈的特性,先进后出,输出正确路径,测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 | NSInteger beginVertex=0; DepthFirstPath *path=[[DepthFirstPath alloc]initWithGraphAndVertex:graph vertex:beginVertex]; for (NSInteger i=0; i<[path.edgeTo count]; i++) { NSLog( @"节点%ld--值为:%@" ,i,path.edgeTo[i]); } for (NSInteger i=0; i<graph.vertexs;i++) { NSMutableArray *data=[path pathTo:i]; NSLog( @"%ld到%ld路径为:%@" ,beginVertex,i,[data componentsJoinedByString: @"--" ]); } NSLog( @"技术交流群:%@" , @"228407086" ); NSLog( @"原文地址:http://www.cnblogs.com/xiaofeixiang" ); |
测试效果:
广度优先搜索
如果我们仔细观察会发现一个现象,图片中我们很明显看到顶点0可以直接到5,我们确发现广度搜索的路径给出的是0-2-3-5,如果想要获取最短路径,界限来一起看一下广度优先搜索,代码与上面的代码也是改动了一部分,比较好懂,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @ interface BreadthFirstPath : NSObject //记录顶点是否被标记 @property (strong,nonatomic) NSMutableArray *marked; //起点 @property (assign,nonatomic) NSInteger beginVertex; //从起点到一个顶点的已知路径上的最后一个顶点 @property (strong,nonatomic) NSMutableArray *edgeTo; //找到与七点vertex所有连通的节点 -(instancetype)initWithGraphAndVertex:(Graph *)graph vertex:(NSInteger)vertex; -( void )breadthSearch:(Graph *)graph vertex:(NSInteger)vertex; -(NSMutableArray *)pathTo:(NSInteger)vertex; //节点是否被标记 -(Boolean)hasPathTo:(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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | @implementation BreadthFirstPath #pragma mark getter and setter -(NSMutableArray *)marked{ if (!_marked) { _marked=[[NSMutableArray alloc]initWithCapacity:1]; } return _marked; } -(NSMutableArray *)edgeTo{ if (!_edgeTo) { _edgeTo=[[NSMutableArray alloc]initWithCapacity:1]; } return _edgeTo; } -(instancetype)initWithGraphAndVertex:(Graph *)graph vertex:(NSInteger)vertex{ self=[super init]; if (self) { for (NSInteger i=0; i<graph.vertexs;i++) { [self.marked addObject:[NSNull null ]]; [self.edgeTo addObject:[NSNull null ]]; } self.beginVertex=vertex; [self breadthSearch:graph vertex:vertex]; } return self; } //http://www.cnblogs.com/xiaofeixiang -( void )breadthSearch:(Graph *)graph vertex:(NSInteger)vertex{ NSMutableArray *queue=[[NSMutableArray alloc]initWithCapacity:1]; self.marked[vertex]=[NSNumber numberWithBool: true ]; [queue addObject:[NSNumber numberWithInteger:vertex]]; while ([queue count]>0) { NSInteger header=[[queue objectAtIndex:0] integerValue]; [queue removeObjectAtIndex:0]; for (NSInteger i=0; i<[graph.adjDataSource[header] count]; i++) { NSInteger temp=[[graph.adjDataSource[header] objectAtIndex:i] integerValue]; if (![self isMarked:temp]) { self.marked[temp]=[NSNumber numberWithBool: true ]; self.edgeTo[temp]=[NSNumber numberWithInteger:header]; [queue addObject:[NSNumber numberWithInteger:temp]]; } } } } -(Boolean)isMarked:(NSInteger)vertex{ return self.marked[vertex]==[NSNull null ]? false :[self.marked[vertex] boolValue]; } -(Boolean)hasPathTo:(NSInteger)vertex{ return self.marked[vertex]==[NSNull null ]? false :[self.marked[vertex] boolValue]; } -(NSMutableArray *)pathTo:(NSInteger)vertex{ if (![self hasPathTo:vertex]) { return NULL; } NSMutableArray *path=[[NSMutableArray alloc]initWithCapacity:1]; for (NSInteger i=vertex; i!=self.beginVertex; i=[self.edgeTo[i] integerValue]) { [path insertObject:[NSNumber numberWithInteger:i] atIndex:0]; } [path insertObject:[NSNumber numberWithInteger:self.beginVertex] atIndex:0]; return path; } @end |
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 | NSInteger beginVertex=0; BreadthFirstPath *breadthPath=[[BreadthFirstPath alloc]initWithGraphAndVertex:graph vertex:beginVertex]; for (NSInteger i=0; i<[breadthPath.edgeTo count]; i++) { NSLog( @"节点%ld--值为:%@" ,i,breadthPath.edgeTo[i]); } for (NSInteger i=0; i<graph.vertexs;i++) { NSMutableArray *data=[breadthPath pathTo:i]; NSLog( @"%ld到%ld路径为:%@" ,beginVertex,i,[data componentsJoinedByString: @"--" ]); } NSLog( @"技术交流群:%@" , @"228407086" ); NSLog( @"原文地址:http://www.cnblogs.com/xiaofeixiang" ); |
测试效果:
作者: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的设计模式综述