强连通图(图指有向图):图中任意两点双向可达
强连通分支:图里与自己内部强连通但与整个图的其他部分不强连通的子图
kosaraju算法通过两次DFS来实现,第一次对有向图D进行搜索,并标记顺序;第二次对有向图D的反图(见有向图的构造revers方法)进行搜索。从构造函数进入dfs函数之后,只要不退出则说明之后遍历的都是连通的。
class kosaraju
{
private:
bool *marked;
int *id;
int count;
void dfs(digraph g, int v)
{
marked[v] = true;
id[v] = count;
for (int w : g.iterator(v))
{
if (!marked[w])
{
dfs(g, w);
}
}
}
public:
kosaraju(digraph g)
{
marked = new bool[g.numv()];
id = new int[g.numv()];
std::fill(marked, marked + g.numv(), 0);
DFO order(g.reverse());
for (auto s : order.rp_order())
{
if (!marked[s])
{
dfs(g, s);
count++;
}
}
}
bool strongly_con(int v, int w)
{
return id[v] == id[w];
}
int in_id(int v) //返回顶点所属的连通分支
{
return id[v];
}
int con_num() //连通分支数
{
return count;
}
};
这段代码的巧妙之处就在于我们使用上一期构造的BFO对象来对图的反图进行第一次遍历,这样如果我们就可以得到遍历之后进行拓扑排序的一个序列。然后我们根据这个序列的顺序来进行遍历,如果我们从构造函数进入DFS之后,不退出,那么后续的点都是强连通的。
证明如下:
dfs(g,s)从构造函数进入,然后进入dfs(v),证明存在s到v的路径s-v,有因为v之前并没有出现,所以在根据g反的拓扑排序中,v在s之后,及在g反中,存在从s到v的路径s-v,即在g中含有从v到路径v-s,即s与v是强连通的。证毕。
运行结果:
明显该图有5个连通分支