kosaraju算法-求解有向图SCC

kosaraju算法用于求解有向图的强连通分量(SCC)。首先从未入栈的顶点开始进行深度优先搜索(DFS),结束后将顶点入栈。然后标记所有顶点为未处理,再次从栈顶取出顶点进行DFS,标记遇到的顶点,得到的连通块即为强连通分量。算法的时间复杂度为O(V+E),且生成的连通块按拓扑排序排列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法流程:

G<V,E>是我们要遍历的有向图,V代表顶点集,E代表边集。S是栈,初始为空

1)while栈中顶点不包含V:

从V中选择一个不在栈中的顶点,开始DFS,每当一个顶点的拓展结束的时候,将其加入栈。

2)将每个顶点标记为0,表示还没有并入联通块,连通块计数器初始为0

3)while 栈不为空:

从栈顶取顶点,直到取得没有被标记的顶点,计数器+1,开始DFS,标记遇到的顶点。

=====================

参考:http://lcm.csa.iisc.ernet.in/dsa/node171.html

简单证明:

如果dfs2时x是root,u是x的子孙。则G中存在u -> x的路径。

=> x在栈中的顺序一定在u后面

=> 1)dfs1中u的递归开始在x之前 2)dfs1中x的递归开始在u前

=> 若1),则u可以访问到x,x的递归在u之前结束,x先入栈,矛盾。

=> 若2),则在x的递归过程中访问了u

=> G中存在 x->u ,则x,u在同一个scc中

--------------------------------------------------------------------------------------------------------

时间复杂度:O(V+E)

重要性质:求出的连通块是按照topo顺序排列的

int used[Maxn], sccNo[Maxn], V;
vector<int> g[Maxn], rg[Maxn], vs;

void dfs(int now) {
    used[now] = 1;
    int sz = g[now].size(), v;
    rep(i, 0, sz-1) {
        v = g[now][i];
        if (!used[v]) dfs(v);
    }
    vs.push_back(now);
}

void rdfs(int now, int flag) {
    used[now]  = 1;
    sccNo[now] = flag;
    int sz = rg[now].size(), v;
    rep(i, 0, sz-1) {
        v = rg[now][i];
        if (!used[v]) rdfs(v, flag);
    }
}

int scc() {
    memset(used, 0, sizeof(used));
    vs.clear();
    //rep(i, 0, V-1)
    //    if (!used[i]) dfs(i);

    urep(i, V-1, 0)
        if (!used[i]) dfs(i);

    memset(used, 0, sizeof(used));
    int cnt = 0;
    urep(i, V-1, 0)
        if (!used[vs[i]]) rdfs(vs[i], cnt++);
    return cnt;
}

void add_edge(int from, int to) {
    g[from].push_back(to);
    rg[to].push_back(from);
}

void test() {
    V = 9;
    rep(i, 0, V-1) {
        g[i].clear();
        rg[i].clear();
    }

    add_edge(1, 2);
    add_edge(2, 4);
    add_edge(3, 2);
    add_edge(4, 0);
    add_edge(4, 5);
    add_edge(4, 6);
    add_edge(5, 3);
    add_edge(5, 6);
    add_edge(5, 7);
    add_edge(6, 7);
    add_edge(7, 6);
    add_edge(7, 8);
    add_edge(8, 0);

    int cnt = scc();
    cout << cnt << " scc\n";
    rep(i, 0, cnt-1) {
        cout << "{ ";
        rep(j, 0, V-1)
            if (sccNo[j] == i) cout << j << ' ';
        cout << "}\n";
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值