圖中最常用到的兩種搜索深度優先搜索和廣度優先搜索,深度優先搜索是一種在開發爬蟲早期使用較多的方法它的目的是要達到被搜索結構的葉結點(即那些不包含任何超鏈接的Html文件) ,廣度搜索屬於一種盲目搜尋法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。換句話說,它並不考慮結果的可能位置,徹底地搜索整張圖,直到找到結果為止。
深度優先搜索
圖中我們經常會遇到一個問題就是圖的連通性,比如說從一個頂點到另外一個頂點,判斷頂點和其他頂點之間的連通性,以下圖為例:
搜索API定義:
@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
實現:
@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
代碼測試:
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和其他節點直接都可以直接連通,我們通過遞歸的方式成功的判斷亮點之間的連通性,頂點直接的連通性是通過邊鏈接的,深度搜索將在此方法上改動然后給出單點之間的路徑,注意是路徑不是最短路徑,我們會發現代碼變動很小。
//深度優先 @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
實現代碼:
@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之間是直接相連,最后輸出路徑的時候利用棧的特性,先進后出,輸出正確路徑,測試代碼如下:
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,如果想要獲取最短路徑,界限來一起看一下廣度優先搜索,代碼與上面的代碼也是改動了一部分,比較好懂,代碼如下:
@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
實現代碼:
@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
測試代碼:
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");
測試效果: