求解有向图的强连通分量的SCC问题---POJ 2186 Popular Cows

【SCC问题】

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected),如果有向图G的每两个顶点都强连通,称G是一个强连通图.通俗的说法是:从图G内任意一个点出发,存在通向图G内任意一点的的一条路径.

非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components,SCC).

求图强连通分量的意义是:由于强连通分量内部的节点性质相同,可以将一个强连通分量内的节点缩成一个点(重要思想),即消除了环,这样,原图就变成了一个有向无环图(directed acyclic graph,DAG).显然对于一个无向图,求强连通分量没有什么意义,连通即为强连通.

【求解算法】

求解有向图强连通分量主要有3 个算法:Tarjan 算法、Kosaraju 算法和Gabow 算法,本文主要介绍Tarjan算法和Kosaraju算法的思想和实现过程。

1.Tarjan算法

0)预备知识

在介绍算法之前,先介绍几个基本概念。


DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树,如图(b)所示。

树边:(又称为父子边),在搜索树中的实线所示,可理解为在DFS过程中访问未访问节点时所经过的边。

回边:(又称为返祖边后向边),在搜索树中的虚线所示,可理解为在DFS过程中遇到已访问节点时所经过的边。

我们用dfn[u]记录节点u在DFS过程中被遍历到的次序号,low[u]记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小),那么low[u]的计算过程如下:



1)基本原理

Tarjan 算法是基于DFS 算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。当dfn(u)=low(u)时,以u 为根的搜索子树上所有节点是一个强连通分量。

算法伪代码如下:

algorithm tarjan is
  input: 图 G = (V, E)
  output: 以所在的强连通分量划分的顶点集

  index := 0
  S := empty    // 置栈为空
  for each v in V do
    if (v.index is undefined)
      strongconnect(v)
    end if

  function strongconnect(v)
    // 将未使用的最小index值作为结点v的index
    v.index := index
    v.lowlink := index
    index := index + 1
    S.push(v)

    // 考虑v的后继结点
    for each (v, w) in E do
      if (w.index is undefined) then
        // 后继结点w未访问,递归调用
        strongconnect(w)
        v.lowlink := min(v.lowlink, w.lowlink)
      else if (w is in S) then
        // w已在栈S中,亦即在当前强连通分量中
        v.lowlink := min(v.lowlink, w.index)
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来寻找有向图的最大连通分量。以下是一个使用DFS来实现的Python代码示例: ```python from collections import defaultdict class Graph: def __init__(self): self.graph = defaultdict(list) def add_edge(self, u, v): self.graph[u].append(v) def DFS(self, v, visited, component): visited[v] = True component.append(v) for i in self.graph[v]: if not visited[i]: component = self.DFS(i, visited, component) return component def find_largest_scc(self): visited = [False] * (max(self.graph) + 1) components = [] for i in range(len(visited)): if not visited[i]: component = [] components.append(self.DFS(i, visited, component)) largest_scc = max(components, key=len) return largest_scc g = Graph() g.add_edge(0, 1) g.add_edge(1, 2) g.add_edge(2, 0) g.add_edge(3, 4) g.add_edge(4, 5) g.add_edge(5, 3) print(g.find_largest_scc()) # 输出 [0, 1, 2] ``` 这个代码示例中,我们首先定义了一个`Graph`类来表示有向图。我们使用`defaultdict`来创建一个空的邻接表。然后我们定义了一个`add_edge`方法来添加边。 接下来,我们定义了一个`DFS`方法来执行深度优先搜索。这个方法用于遍历与给定节点v相连的所有节点,并将它们添加到`component`列表中。我们还定义了一个`visited`列表,用于记录已经访问过的节点。 最后,我们定义了一个`find_largest_scc`方法来查找有向图的最大连通分量。我们遍历所有节点,如果我们遇到一个未访问过的节点,我们就执行DFS,并将找到的连通分量添加到`components`列表中。最后,我们找到`components`列表中最大的那个连通分量,即为有向图的最大连通分量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值