证明比较繁琐,仔细检查了应该没有大错,记录一下证明过程。
强连通分量定义:
在有向图中,强连通分量的定义是:有向图的某个子图,其中任意两个点之间可以互达。
强连通分量的定理:
定理2:子图是强连通分量<=>子图中的每一条路径都归属于一个环状(除非只有一个点)。
证明:根据强连通分量的定义,任意两个点之间可以互达,所以等价于任意两个点之间的路径是一个环的一部分。
tarjan算法:
假设图有n个节点,m条边。该算法可以在O(n+m)的时间复杂度中输出所有强连通分量。
Tarjan算法是在深度优先搜索的基础上进行的。
首先算法有3个数据结构:
Dfn[n] 存储每一个节点访问过程中的时间戳time_stamp
Low[n] 存储每一个节点在深度优先搜索的子孙中time_stamp的最小值(搜索的过程中会由于遇到回路,访问到之前访问过的节点,所以子孙的time_stamp可能比该节点小)
Stack,存放已经访问的节点。
Tarjan算法流程:
使用递归的方法进行深度优先搜索
int tarjan( int index ){
dfn[index] = low[index] = time_stamp++;
stack.push_back(index);//入栈
for(){//遍历这个邻接表中这个点的邻接点
假如邻接点的下标是v
if(v在stack中){
low[index] = min(dfn[v],low[index])
} else if(v没在stack中 && 没有访问过){
low[v] = tarjan(v)
low[index] = min(low[v],low[index])
}
}
if( dfn[index] == low[index] ){
for( ){
把stack中index以及以上的点出栈,作为一个强连通分支
}
}
return low[index];
}
Tarjan算法原理:
1.为什么红字部分出栈的是一个强连通分支:
根据定理2,等价于证明,出栈的部分,节点路径都是某个环的一部分。
已知:目前栈内的将出栈部分,假如节点下标为i,则一定有dfn[i]>=low[i](当且仅当i=最后一个出栈的节点下标时取等号)。
运用反证法:假如有某条路径不属于某个环,如下图中的路径B->A,则这段路径的末端点A的dfn[A] = low[A],且该点是出栈的某条路径的末端点,而不是初始点,所以不可能是最后出栈的那个点,和已知矛盾。所以,出栈的任意一条路径都属于某个环,即出栈的子图是一个强连通分量。
假设出栈的部分不完整,则本应该在这次出栈的点可能存在于栈的哪些部分呢?
1.之前出栈的部分
2.还没有入栈的部分
3.还没有出栈的部分
首先看2,不可能。因为假如没有入栈,说明这些点没有在这棵深度优先搜索树中,假如这些点在本该在该强连通分量中,则和定理1相违背,所以情况2中不可能包含本应该出栈的强连通分量中的点。
再看3,也不可能。3中的点的dfn 和 low都分别小于本次出栈的点的dfn和low,也就说明本次出栈的点都无法访问到还没有出栈的点,所以情况3中不可能包含本应该出栈的强连通分量中的点。
最后看情况1,其实情况1和情况3是类似的,之前出栈的部分A如果和本次出栈的强连通分量B可以组成更大的强连通分量,这就等价于,以之前出栈的强连通分量A为视角,亦是说A是不完整的,A中缺少的部分在还未出栈的节点和还没有访问的节点之中。这和之前的情况2,情况3推导矛盾,所以,情况1也不可能。
所以综上所述,红字部分出栈的强连通分支是完整的。