Gracekite's Homepage

Back

用这篇博客记录一下学习Tarjan缩点的风风雨雨

三种缩点的时间复杂度均为

定义: 时间戳:表示遍历到之前遍历过多少个点 追溯值:表示从出发在不经过父子边的情况下能到达的最早的祖先的值。

有向图#

强联通分量缩点(SCC)#

SCC的作用是将一个有向有环图的环缩成一个点,使其形成一个DAG,方便后续的运算。

伪代码#

Tarjan(当前节点):
    //入
    初次进入当前节点 给当前节点赋新的 时间戳 和 追溯值
    将当前节点入栈
    //回
    遍历 当前节点的所有 孩子:
        如果 节点无时间戳(未被访问):
            Tarjan(孩子)
            用 孩子的追溯值 更新 当前节点的追溯值
        如果 当前节点在栈中:
            用 孩子的时间戳 更新 当前节点的追溯值
    //离
    如果 当前节点的时间戳 等于 追溯值:(说明这里已经形成了一个强联通分量)
        弹栈(此时弹出来的都是强联通分量中的点) 直到 把当前节点弹出来
plaintext

Model Code#

无向图#

无向图有两个缩点方式:点双 和 边双;

解释一下两者的区别:

点双边双
定义在一张联通的无向图中,对于两个点和,如果无论删去哪个点(和本身除外),都不能使它们不联通,我们就说和点双联通在一张联通的无向图中,对于两个点和,如果无论删去哪条边,都不能使它们不联通,我们就说和边双联通
性质图中任意一个割点都在至少两个点双中
任意一个不是割点的点都只存在于一个点双中
割边不属于任意边双,而其它非割边的边都属于且仅属于一个边双。
对于一个边双中的任意两个点,它们之间都有至少两条边不重复的路径。

点双联通分量缩点(v-DCC)#

伪代码#

这个点不是割点,但没有孩子能走到他的祖先节点,若不特判将被判成割点

这个点是割点,因为它有两个孩子

Model Code#

边双联通分量缩点(e-DCC)#

Tarjan(当前节点, 入边):
    初次进入当前节点 给当前节点赋新的 时间戳 和 追溯值
    将当前节点入栈
    遍历 当前节点的所有边:
        如果 节点无时间戳(未被访问):
            Tarjan(边所指的孩子,当前边)
            用孩子的追溯值更新当前节点的追溯值
            如果 孩子的追溯值 大于 当前节点时间戳:(回不到当前节点了)
                当前边及其反边就是割边
        否则如果 当前边不是 入边的反边:(即 不回到当前节点的父亲):
            用孩子节点的时间戳更新当前节点的追溯值(不通过父边可能能走到更早的祖先)
    如果 当前节点的时间戳 等于 当前节点的追溯值:(只能到自己)
        新建边双
        弹栈直到把自己弹掉
plaintext
Tarjan缩点
https://betteroier.github.io/blog/tarjan%E7%BC%A9%E7%82%B9
Author Yuxuan Zheng
Published at August 30, 2023