Kosaraju算法的证明

本文深入探讨了Kosaraju算法的原理、证明及其特点,包括如何通过图的转置找到强连通分量,并解释了算法求出的分量具有拓扑排序的性质。文章详细解析了算法流程,通过数学证明确保理解的准确性。
Kosaraju算法的证明

首先提出图的转置的概念。所谓转置就是将一个图上所有的有向边反向。简单来说就是本是x->y的一条边,现在变为y->x这样一条边。

另外强连通性质具有传递性,如果(i,j),(j,k)属于同一强连通分量,那么(i,k)属于同一强连通分量。因为如果满足题设,那么存在路径i->j->k和k->j->i。所以传递性得证。所以其实我们要求点i所属的极大强连通分量,只需要把所有和i可以互达的点求出来就可以了。

该算法的流程如下:

1、 利用深度优先搜索遍历原图,并按出栈的先后顺序把点放进一个线性表里。这里可以每次取任意一个点出发DFS,如果还有点未被DFS到,那么继续任选未被遍历到的点出发DFS。

2、 将整个图转置。

3、 按照线性表中的出栈顺序,从后往前依次作为起点DFS。每次DFS到的就是一个强连通分量。

初看这个算法很神奇,但是其实是不难证明的。

命题1:该算法求出的都是强连通分量

假设在后来转置后的图中从x dfs到了 y处,说明存在路径x->y。因为这是在转置图中,所以说明原图中存在路径y->x

然后另外一个信息就是x的序号在y之后。这有两种可能:

1、以y为根先DFS出了一棵搜索树(可以认为是整个搜索树的一棵子树),但是这棵子树里不包含x,并且此时x还未被dfs到。(利用反证法,如果这棵子树里包含了x,那么x的序号会在y之前)

2、y是x扩展出来的搜索树中的一个结点。

综合两个条件,综合两个条件取交。那么上面两种可能中的第一种不成立。因为存在路径y->x,所以如果x未被dfs到,一定会被y为根的搜索树包含的。于是只剩下第二种可能,那么第二种情况表明存在路径x->y。所以x,y可以互相到达。至此证明了该算法求出的都是强连通分量。命题1得证。

命题2:所有的极大强连通分量都会被该算法求到。

命题2等价于:存在两个点(i,j),他们互相可达,但是没有被放进同一个强连通分量中。易知若(i,j)可以互相到达,那么肯定其中一个点在另外一个点扩展出去的搜索树中(注意DFS的性质,走完一个分叉再走另外一个分叉)。

由于轮换对称,不妨设j在i扩展出去的搜索树中,那么显然j比i先出栈。假如命题2不成立,那么必须是有一棵以A为根(而且这个A必须比i后出栈)的搜索树,它包含了j但不包含i。由命题1可知,(A,j)可互达。那么由于(i,j)可互达,在这颗搜索树中,肯定能有j扩展到i,所以这样的一棵搜索树是不存在的。因此反证得命题2成立。

该算法比Tarjan算法慢一点,但是有一个好处:该算法依次求出的强连通分量已经是拓扑序的。

下面给出这一性质的证明:

对于两个不同的强连通分量A,B,设A中出栈顺序最晚的点为a,B中出栈顺序最晚的点为b。不妨设a出栈顺序在b之前,那么有两种可能。

1、存在路径b->a。由于两点不属于同一强连通分量,所以不存在路径a->b。这种情况下Kosaraju算法会先把B强连通分量拿出来,所以是满足拓扑序的。

2、不存在路径b->a。那么这种情况下必然也不存在路径a->b,否则a出栈之时,b必然已经出栈了。所以,先拿出B强连通分量是符合拓扑序的。

所以,该性质得证。

【写图论教程时无聊证了一下。。。各位无视】


Kosaraju算法的正确性证明基于有向图中强连通分量(SCC)的性质以及深度优先搜索(DFS)在图上的行为。该算法通过两次DFS操作,结合图的转置,确保能够正确识别出图中所有的强连通分量。其核心思想在于利用转置图的DFS后序排列来决定原图DFS的访问顺序,从而保证每个强连通分量被完整地识别出来。 在第一次DFS中,对原图的转置(即所有边的方向反转后的图)进行深度优先搜索,并按照每个节点完成搜索的时间顺序记录后序排列(post-order)。这个后序排列中的节点顺序在第二次DFS中起到关键作用。具体来说,第二次DFS是在原图上进行的,但节点的访问顺序是根据第一次DFS得到的后序排列逆序来进行的。这种策略确保了每次DFS调用都能完整地访问一个强连通分量中的所有节点[^3]。 证明正确性的关键在于理解强连通分量在DFS中的行为。假设两个节点u和v属于同一个强连通分量,那么在转置图中,它们之间的路径仍然存在,只是方向发生了变化。第一次DFS在转置图中进行,确保了每个强连通分量的“根节点”(即该分量中在DFS过程中最后被访问的节点)在后序排列中具有较高的位置。在第二次DFS中,按照后序排列的逆序访问节点,可以确保每个强连通分量的节点被一次性访问完毕[^1]。 进一步地,可以通过反证法来证明Kosaraju算法的正确性。假设存在两个节点i和j,它们属于同一个强连通分量,但在最终的SCC识别结果中没有被归为一组。根据DFS的性质,如果i比j先完成搜索,则i会在后序排列中出现在j之前。然而,由于i和j互达,那么在转置图中也互达,这意味着在第一次DFS中,j应该比i更早完成搜索,这与假设相矛盾。因此,可以得出结论,Kosaraju算法能够正确地识别出所有属于同一强连通分量的节点[^5]。 ### Kosaraju算法实现示例 以下是一个简单的Kosaraju算法实现示例,用于识别有向图中的强连通分量: ```python def kosaraju_scc(graph): visited = set() order = [] def dfs1(node): visited.add(node) for neighbor in graph[node]: if neighbor not in visited: dfs1(neighbor) order.append(node) # First pass: compute finishing times in reverse graph for node in graph: if node not in visited: dfs1(node) # Transpose the graph transposed_graph = {node: [] for node in graph} for node in graph: for neighbor in graph[node]: transposed_graph[neighbor].append(node) visited = set() scc_list = [] def dfs2(node, component): visited.add(node) component.append(node) for neighbor in transposed_graph[node]: if neighbor not in visited: dfs2(neighbor, component) # Second pass: process nodes in order of decreasing finishing times while order: node = order.pop() if node not in visited: component = [] dfs2(node, component) scc_list.append(component) return scc_list ``` 在上述代码中,`graph`表示原图,是一个字典,其中键是节点,值是该节点指向的其他节点列表。`dfs1`函数用于计算转置图中的DFS后序排列,`dfs2`函数则在转置图上进行DFS,以识别强连通分量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值