强连通分量

本文介绍了强连通分量的概念,包括强连通图和强连通分量的定义,并重点讲解了Tarjan算法用于求解强连通分量的原理和步骤。文中提供了一个简化的算法概述,以及Tarjan算法的Python实现模板,适用于有向图的分析。
摘要由CSDN通过智能技术生成

概念:

  1. 强连通:有向图中两个结点如果能两两互相到达,就称这两个点强连通。
  2. 强连通图:有向图中所有结点都能两两到达,就称强连通图。
  3. 强连通分量:非强连通的有向图的极大强连通子图,称为强连通分量(SCC即Strongly Connected Componenet)。一个图中可以有多个。
  4. 强连通分量是一个环或者一个点。

求强连通分量一般用 Korasaju算法Tarjan算法
这里我就只记录tarjan算法。

这里有一篇大神的文章,我觉得讲的非常好。
https://www.cnblogs.com/five20/p/7594239.html
看他里面的tarjan算法的图步骤。

Tarjan算法:

简述:
深搜,如果碰到叶子结点(没有出度的结点),叶子结点就算一个连通分量,如果碰到之前走过的结点v(构成回路了),就回溯,回溯到这个结点v,这段回溯的结点又构成一个强连通分量。

理解:
碰到之前搜的结点还不能立刻回溯,因为不能保证这个是极大强连通子图,所以这是要用两个变量dfn[u], low[u], dfn[u] 记录遍历到u结点时的时间(时间戳),low[u]记录碰到之前走过的结点的最小值。所以碰到之前走过的结点的时候就和之前的low比较取最小值,这样就能保证回路回到的是最前面走的结点,这样强连通子图就是最大的。回溯的时候也要更新low[u],保证

最普遍的写法:
用栈存深搜中的点,遇到叶结点就出栈,遇到回路也而且回溯到根节点就把根节点以及之前的元素出栈。这里就用到 dfn[u] == low[u] 来判断是否要出栈了。相等表示回溯已经到达根节点,就要弹出元素。弹出的时候记录一下就好了。

变量:
数组 dfn[] 和 low[]:表示 遍历到某个结点的时间点 和 深搜树中结点的根节点(可以用来保证是最前面的根)
stack:栈,存储深搜过程中的结点。
in_stack[]:标记数组,标记结点是否在栈中。
idx:时间戳
一个存强连通分量的数据结构:看具体使用。
bcnt:强连通分量的数量。
vis:标记数组,标记结点是否已访问,这个可有可无,因为代码中可以中dfn来替代,dfn中0代表未访问,大于零表示访问过。

步骤:
深搜,
时间戳加一,
设置dfn[u]和low[u]的初始值为时间戳的值,dfn[u] = low[u] = idx,
u点入栈,标记u点入站
遍历u点的子结点:
如果没访问过就继续递归深搜,深搜完回溯时,要更新low[u]
访问过而且在栈中,表示遇到回路了,就更新low[u]
访问完子结点就判断是否要出栈。 dfn[u] == low[u] 就出栈。
强连通分量的数量加一,用一个数据结构存储当前的分量。这里我用了python的字典{k: id , v: 列表}。

伪代码:伪代码出自这本书中《Competitive Programming 3》

vi dfs_num, dfs_low, S, visited; // global variables

void tarjanSCC(int u) {
   
	dfs_low[u] = dfs_num[u] = dfsNumberCounter++; // dfs_low[u] <= dfs_num[u]
	S.push_back(u); // stores u in a vector based on order of visitation
	visited[u] = 1;
	for (int j = 0; j < (int)AdjList[u].size(); j++) {
   
		ii v = AdjList[u][j];
		if (dfs_num[v.first] == UNVISITED)
			tarjanSCC(v.first
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值