连通分量
1.连通图和连通分量:
连通图:无向图中,如果顶点vi到vj有路径,则称vi和vj是联通的,如果图中任意两个顶点都是连通的,则称G为连通图。
连通分量:无向图G的极大连通子图称为G的连通分量。极大连通子图的意思是:该子图是G的连通子图,如果再加入一个顶点,该子图不连通。下图中该图有三个连通分量。 对于连通图,则其连通分量就是他自己,对于非连通图,则有2个以上的连通分量。
2.强连通图和强连通分量
弱连通或者单向连通就是指只有一个方向相连.
3.无向图的桥与割点
在生活中,桥就是连接河两岸的交通要道,桥断了,河两岸不在连通。在图论中,桥有着同样的含义。如图所示,去掉该边(5,8)后图分裂成两个互不连通的子图,边(5,8)就为图G的桥,同样,边(5,7)也是图G的桥
4. 无向图的双连通分量
如果无向图中不存在桥,则称它为边双连通图,边连通图中,任意两个点间,存在两条或以上的路径,且路径上的边不重复
- 比如说一个环路
如果无向图中不存在割点,则称它为点双连通图,点连通图中,如果顶点数大于2,则任意两个点间,存在两条或以上的路径,且路径上的边不重复
- 比如一个环路
无向图的极大边双连通子图称为双连通分量,记为e-DCC
无向图的极大点双连通子图称为点双连通分量,记为v-DCC
二者统称为双连通分量DCC
5. 双连通分量的缩点
e-DCC缩点
把每一个边双连通分量e-DCC看作是一个点,把桥看作连接两个缩点的无向边,得到一个树,这种方法称为e-DCC缩点,如图,生成了一棵树
注意:边双连通分量就是删除桥之后留下的连通块,但点双连通分量并不是删除割点后留下的连通块
v-DCC缩点
把每一个点双连通分量v-DCC看作是一个点,把割点看成一个点,每个割点向包含他的v-DCC连接一条边,得到一个树, 如图, 这种方法称为v-DCC缩点。
如图所示,图G的点双连通分量有4 个(没有割点才能叫点双连通分量)
那么,我们得到的树就是:
Tarjan算法
- 无向图的桥.
- 无向图的割点
- 有向图的强连通分量
Tarjan 计算机科学家, 以LCA,强连通分量等算法闻名。Tarjan设计了求解应用领域的许多问题的广泛有效的算法和数拆结构。他以在数据结构和图论上的开创性工作而闻名, 他的 些著名的算法包括Tarjan最近公共祖先离线算法,Tarjan的强连通分量算法以及Link-Cut-Trees环法等。其中Hopcroft-!Tarjan平面嵌入算法是第一个线性时间平面算法。Tarjan 也开创了重要的数据结构,如斐波纳契堆和splay树, 另一项重大贡献是分析了并查集。 他是第一个证明了计算反阿克曼函数的乐观时间复杂度的科学家。
在介绍算法之前,首先引入时间戳和追溯点的概念
时间戳:dfn[u]表示u结点深度优先遍历的序号
追溯点:low[u] 表示u结点或u的子孙能通过非父子边追溯到的dfn最小的节点序号。即回到最早的过去
例如,在深度优先搜索中。每个点的时间戳和追溯点求解过程如下。
初始时,dfn[u] = low[u] ,如果该节点的邻接点未被访问,则一直使用深度优先遍历,1-2-3-5-6-4,此时4的邻接点1已经被访问,且1不是4的父节点,4的父节点是6(深度优先搜索树上的父节点)
深度优先搜索书,起点就是根
无向图的桥
如何判断一条边是桥?
无向边(x,y) 是桥, 当且仅当搜索树上存在x的一个子节点y ,满足:
$low[y]>dfn[x]$
上图中边(5,7), 5的子节点7,满足$low[7]>dfn[5]$ ,因此这条边为桥。
算法
1 |
|
测试数据:
测试数据1 | 测试数据2 | ||
---|---|---|---|
7 | 7 | 10 | 13 |
1 | 4 | 1 | 2 |
1 | 2 | 1 | 3 |
2 | 3 | 1 | 4 |
3 | 5 | 2 | 3 |
5 | 7 | 2 | 5 |
5 | 6 | 5 | 3 |
6 | 4 | 5 | 7 |
5 | 6 | ||
5-7是桥 | 5 | 8 | |
6 | 4 | ||
8 | 9 | ||
8 | 10 | ||
9 | 10 | ||
5-8是桥 | 5-7是桥 |
割点判定法则
若x不是根节点,则x是割点,当且仅当搜索树上存在x的一个子节点y,满足:
$low[y]\geq dfn[x]$ (注意,刚才求桥的是大于符号,这里是大于等于符号)
若x是根节点,则x是割点,当且仅当搜索树上存在至少两个子节点,满足上述条件。因为我们想一下,如果只有一个子节点满足上述条件,那么就如下图的1,2结点,那么1就不是割点。
图中,5的子节点7,满足low[7]>dfn[5]因此5是割点
第二张图中,1是割点
算法
1 |
|
有向图的强连通分量
算法步骤:
- 深度优先遍历结点,第一次访问结点x时,将x入栈,且dfn[x] = low[x] =++num
- 遍历x的所有邻接点y,
- 若y没有被访问,则递归访问y,返回时更新low[x] = min(low[x],low[y])
- 若y已经被访问且在栈当中,则令low[x] = min(low[x],dfn[y]);
- x回溯之前,判断如果low[x] = dfn[x] ,则从栈中不断弹出节点,一直到x出栈为止。弹出的结点就是一个强连通分量
1 |
|
gif来自:https://visualgo.net/zh/dfsbfs
连通分量: 0
连通分量: 1,2,3,4
连通分量: 5,6,7