算法-强连通分量和Kosaraju算法

有向图中,连通性比较好理解,如果两个顶点V和顶点W是可达的,可以称之为强连通的,即存在路径A→B,同时也存在一条有向路径B→A.从之前的有向环的判定过程中其实我们可以得到一个结论就是两个是强连通的当且仅当它们都在一个普通的有向环中。强连通将所有的顶点分为了不同的集合,每个集合都是由相互均为强连通性的顶点的最大子集组成的,我们将这些集合称之为强连通分量。

基础概念

一般来说技术服务于生活,如果将我们看到网页作为顶点,页面指向另外一个页面的超链接作为边,可以将数量庞大的网页分为不同的大小进行处理,作为软件工程师或者说码农,经常遇到的就是模块的封装,如果将模块作为顶点,模块之间的引用作为边,通过强连通图我们可以更好进行模块之间的调用关系考虑适时的解耦。如果通过平方级别的算法解决强连通分量,那么遇到大型有向图的过程我们就会有点力不从心。Kosaraju的算法(也称为Kosaraju-Sharir算法)是线性时间的算法来解决有向图中的连通性查询以及处理强连通分量的数量。

采用之前有向环中的图片:

API定义:

@interface KosarajuCC : NSObject

//记录顶点是否被标记
@property  (strong,nonatomic)  NSMutableArray  *marked;

@property (assign,nonatomic)  NSInteger count;//连通的分量

@property  (strong,nonatomic)  NSMutableArray  *ids;//顶点所在的连通分量的标识符

//连通分量递归初始化
-(instancetype)initWithGraph:(Digraph *)graph;

-(void)depthSearch:(Digraph *)graph  vertex:(NSInteger)vertex;
//判断两个顶点之间是否存在连通性
-(BOOL)stronglyConnected:(NSInteger)vertex  otherVertex:(NSInteger)otherVertex;

@end

算法实战

通过API的定义,如果对比之前之前无向图中的API,我们发现基本上没有变化,具体实现的过程中变化也很小,需要之前基于深度优先搜索的顶点排序,取出逆后序集合进行遍历即可,之后和无向图中一样进行递归判断存储在数组中。

@implementation KosarajuCC

#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:(Digraph *)graph{
    self=[super init];
    if (self) {
        for (NSInteger i=0; i<graph.vertexs;i++) {
            [self.marked addObject:[NSNull null]];
            [self.ids addObject:[NSNull null]];
        }
        DepthFirstOrder *order=[[DepthFirstOrder alloc]initWithGraph:[graph reverse]];
        //遍历图的顶点
        for (NSInteger j=0; j<[order.reversePostStack count]; j++) {
            NSInteger  temp=[[order.reversePostStack objectAtIndex:j] integerValue];
            if (![self isMarked:temp]) {
                [self depthSearch:graph vertex:temp];
                self.count++;
            }
        }
    }
    return self;
}
//博客园-FlyElephant:http://www.cnblogs.com/xiaofeixiang/
-(void)depthSearch:(Digraph *)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];
}

-(BOOL)stronglyConnected:(NSInteger)vertex  otherVertex:(NSInteger)otherVertex{
    return [self.ids[vertex] integerValue]==[self.ids[otherVertex] integerValue];
}
@end

测试代码:

        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];
        KosarajuCC  *graphCC=[[KosarajuCC 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:@"--"]);
        }
        NSInteger  vertex=0,otherVertex=1;
        Boolean  cc=[graphCC stronglyConnected:vertex otherVertex:otherVertex];
        NSLog(@"节点%ld和节点%ld %@强连通的",vertex,otherVertex,[email protected]"是":@"不是");
        NSLog(@"技术交流群:%@",@"228407086");
        NSLog(@"博客园-FlyElephant:http://www.cnblogs.com/xiaofeixiang");

测试结果:

时间: 2024-11-02 23:54:36

算法-强连通分量和Kosaraju算法的相关文章

[图论] 有向图强连通分量 (kosaraju算法,Tarjan算法)

记录自己的想法:在有向图中,如果一些顶点中任意两个顶点都能互相到达(间接或直接),那么这些顶点就构成了一个强连通分量,如果一个顶点没有出度,即它不能到达其他任何顶点,那么该顶点自己就是一个强连通分量.在用kosaraju算法和Tarjan算法求强连通分量的时候,就是给所有的顶点分组染色,同一种颜色的顶点在同一个强连通分量中,记录有多少种颜色(有多少个强联通分量),每个顶点属于哪种颜色(每个顶点在哪个强连通分量重).在同一个强连通分量中的所有顶点可以缩为一个顶点,然后根据缩点构造DAG(有向无环图

强连通分量分解 Kosaraju算法 (poj 2186 Popular Cows)

poj 2186 Popular Cows 题意: 有N头牛, 给出M对关系, 如(1,2)代表1欢迎2, 关系是单向的且可以传递, 即1欢迎2不代表2欢迎1, 但是如果2也欢迎3那么1也欢迎3. 求被所有牛都欢迎的牛的数量. 限制: 1 <= N <= 10000 1 <= M <= 50000 思路: Kosaraju算法, 看缩点后拓扑序的终点有多少头牛, 且要判断是不是所有强连通分量都连向它. Kosaraju算法,分拆完连通分量后,也完成了拓扑序. /*poj 2186

Kosaraju算法——强连通分量

′有向图的极大强连通子图,称为强连通分量. ′子图指的是选取V的一个子集V',以及E当中所有满足u,v∈V'的边集E'所指代的图. ′我们需要找出一幅有向图当中的所有强连通分量. ′一个最朴素的算法: ′构造一个传递闭包(也就是数组Aij表示i能否到达j),然后把Aij=Aji=1的节点置于同一个强连通分量当中 ′这个算法的复杂度是O(n^3),优点是代码复杂度小,缺点是速度太慢了 ′这个算法原理和上面的方法是类似的,如果A能到达B并且B能到达A,那么A和B在同一个强连通分量里面. ′也可以等同

【转载】有向图强连通分量的Tarjan算法

from byvoid [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为

有向图强连通分量的Tarjan算法(转)

原文地址:有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强

有向图强连通分量的Tarjan算法——转自BYVoid

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M).更好的

有向图强连通分量的Tarjan算法

有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,

【转】有向图强连通分量的Tarjan算法

原文地址:https://www.byvoid.com/blog/scc-tarjan/ [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量

[转]有向图强连通分量的Tarjan算法

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M).更好的