Kosaraju-Sharir算法的Wikipedia summary如下:
设G是有向图,S是空堆栈。
- 虽然S不包含所有顶点。
- 选择不在S中的任意顶点v。
- 从v开始执行深度优先搜索。每次深度优先搜索完成扩展顶点u时,将u推到S上。
- 反转所有弧的方向以获得转置图。
- 虽然S是非空的:
- 从S
弹出顶部顶点v- 在转置图中从v开始执行深度优先搜索。
- 访问顶点集将给出包含v的强连通分量;记录这个并从图G和堆栈S中删除所有这些顶点。等效地,可以使用广度优先搜索(BFS)代替深度优先搜索。
但在我的教科书--Sedgewick的Algorithms (fourth edition)中 - 它描述了算法的步骤如下:
- 给出有向图G,计算其反向图的反向后序。 ģ - [R
- 在G上运行标准DFS,但考虑刚刚计算的顺序中未标记的顶点而不是标准的数字顺序
- 所有顶点的集合......
在最后一步中得出的结论是相同的,在它之前的操作中执行的操作也是相同的 - 但似乎这些操作以不同的顺序给出:维基百科告诉我首先在G上执行DFS并且然后转置它,在G R 上做第二个DFS,而我的教科书建议我从转置开始,在G R 上做第一个DFS并且第二个关于G.
我的主要问题是:我是否正确理解了这一点,还是我误解了其中一个人在说什么?
其次:直观地说,似乎这些操作是传递性的,因此这两种"不同的方法"实际上是等价的,并且总会产生相同的最终结果。我已经在几个有向图上测试了这种直觉,它似乎也适用 - 但它是吗?
第三:假设是,是否有任何客观理由偏爱另一方,或者仅仅是偏好问题?
答案 0 :(得分:2)
IMO这两种算法是等价的,但存在细微差别。不同之处在于SCC(强连接组件)输出的顺序。
假设我们有一个非循环的图表,SCC按顺序排列为S1,S2,S3,S4。
S1 -> S2 -> S3 -> S4
算法1(维基百科)。
当你构建堆栈S时,对于任何顶点v,v之后的所有顶点都应该进入v之前的堆栈S,因为我们正在前向图上执行DFS。
现在我们反转图表:
R_S1 <- R_S2 <- R_S3 <- R_S4
要从堆栈S
中弹出的第一个顶点应位于R_S1
中。因此,当您执行DFS时,要输出的第一个SCC应为R_S1
。
算法2(书)。
这里我们首先反转图表:
R_S1 <- R_S2 <- R_S3 <- R_S4
现在当我们在任何顶点v
上进行DFS时,v
之前的顶点(在原始图中)应该在v之前有序。此外,因为我们开始来自N
的order_index然后递减order_index,v
之后的所有顶点的拓扑排序都应低于v。
原图:
S1 -> S2 -> S3 -> S4
最低有序顶点现在应在S4
。因此,从图中输出的第一个SCC应为S4
,而不是第一种情况下的R_S1
。