Kosaraju 算法
一.算法简介
在计算科学中,Kosaraju算法(又称为 Sharir Kosaraju算法 )是一个线性时间(linear time) 算法, 用于找到的有向图的强连通分量。它利用了一个事实,逆图(与各边方向相同的图形反转, transpose graph)与原始图有相同的强连通分量。
逆图(Tranpose Graph )
我们对逆图定义如下:
若
上图是有向图
通过以上的描述我们发现,Kosaraju 算法就是分别对原图
-
.对原图
进行深度优先搜索,找出每个节点的完成时间(时间戳) -
.选择完成时间较大的节点开始,对逆图
搜索,能够到达的点构成一个强连通分量 -
.如果 所有节点未都被遍历,重复2). ; 否则算法结束;
二.算法图示
上图是对图 u.d
和 完成时间 u.f
我们按照 结束时间戳 由小到大 压入栈中
-
每次从栈顶取出元素
-
检查是否被访问过
-
若没被访问过,以该点为起点,对逆图进行深度优先遍历
-
否则返回第一步,直到栈空为止
[ATTENTION]
对逆图搜索时,从一个节点开始能搜索到的最大区块就是该点所在的强连通分量。
从节点1出发,能走到 2 ,3,4 , 所以{1 , 2 , 3 , 4 }是一个强连通分量
从节点5出发,无路可走,所以{ 5 }是一个强连通分量
从节点6出发,无路可走,所以{ 6 }是一个强连通分量
自此Kosaraju Algorithm完毕,这个算法只需要两遍DFS即可,是一个比较易懂的求强连通分量的算法。
三.算法复杂度
-
邻接表:
-
邻接矩阵:
该算法在实际操作中要比Tarjan算法要慢
四.算法模板&注释代码
#include <bits/stdc++.h>
using namespace std;
// 节点
struct node {
int next; // 下一个节点的索引位置
int to; // 当前节点的节点编号
};
const int N = 1e4 + 5; // 节点个数最大
const int M = 2e4 + 5; // 边的个数最大
int n, m;
// 因为是两张图 G 和 G^T 所以 统一加一维
// 数组模拟邻接表建图
int head[2][N];
node ver[2][M];
int cnt[2]; // 节点个数
int top = 0, sta[N]; // 栈顶指针 和 站 用来储存时间戳
bitset<N> vis;
int color[N]; // 染色数组, 用来记录哪些节点在一个连通块中
int sz[N]; // 各个连通块的大小
int tot = 0; // 连通块个数
int colorIndex = 0; // 颜色种类
// 加边(起点, 终点, 正图||逆图)
void addEdge(int x, int y, int _) {
ver[_][++cnt[_]].to = y;
ver[_][cnt[_]].next = head[_][x];
head[_][x] = cnt[_];
}
// 第一次 DFS 按照时间戳入栈
void firstDFS(int x, int _) {
vis[x] = 1;
for (int i = head[_][x]; i; i = ver[_][i].next) {
int y = ver[_][i].to;
if (vis[y]) continue;
firstDFS(y, _);
}
sta[++top] = x; // 结尾入栈
}
// 第二次 DFS 搜索 x 节点可以到达的点的集合, 即一个强连通块
void secondDFS(int x, int _) {
sz[tot]++;
vis[x] = 1;
color[x] = colorIndex; // 染色
for (int i = head[_][x]; i; i = ver[_][i].next) {
int y = ver[_][i].to;
if (vis[y]) continue;
secondDFS(y, _);
}
}
int main() {
ios::sync_with_stdio(0);
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
addEdge(u, v, 0); // G 加 正向边
addEdge(v, u, 1); // G^T 加 反向边
}
// 对原图的 DFS
for (int i = 1; i <= n; i++)
if (!vis[i]) firstDFS(i, 0);
vis &= 0; // 清空 vis[]
// 按时间戳对逆图进行 DFS
while (top > 0) {
int tmp = sta[top--];
if (vis[tmp]) continue;
tot++, colorIndex++;
secondDFS(tmp, 1);
}
// 输出 强连通块
for (int i = 1; i <= tot; i++) {
for (int j = 1; j <= n; j++)
if (color[j] == i) cout << j << " ";
cout << endl;
}
return 0;
}
本文作者:咕噜咕噜酱
本文链接:https://www.cnblogs.com/syqwq/p/15145317.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步