Tarjan缩点
引入
- 有向图的强连通:图中任意两点可以互相到达
- 那么什么是强连通分量?
- 就是指有向图中的一部分构成强连通,称这部分为强连通分量,下图中的B,C,D,就构成了强连通分量。
- Tarjan缩点算法就是为了找到有向图强连通分量
实现
- 算法依靠dfs,用一个dfn[] 数组,一个low[] 数组,以及一个栈实现上述功能。
- 时间戳,即,访问有向图一个节点的时刻。如下图,dfs后A的时间戳为1,B的时间戳为2,C为3,E为4,D为5,F为6,以此类推。这个即为dfn[] 数组所存的数据。作用:判断一个节点是否被访问过。
- low[] 数组,存放能到达的最靠前的点的时间戳,初始值指向本身的时间戳。如下图,左边的数字为dfn,右边为low,C,D的low值都指向B的时间戳2。用途,如图D指向了B而他们的low值一样,不难发现,有一个环,所以low是用来判环的。
- 那么low的值是如何赋予的?很简单,用该点指向的点的low值更新,也就是取一个min。
- 最后找到了环,怎么取出呢?这就用到了栈。
流程
1.for循环寻找一个dfn为0的点,将该点dfn和low值均赋予时间戳的值,时间戳值再加1;
2.将该点压入栈
3.寻找该点指向的点,判断是否访问过
4.如果没有访问,重复1~3递归,然后更新该点low值
5.如果访问过直接更新low值
6.回溯过程中,找到第一个dfn与low相同的点,该点即为环的一点,它的子树就构成一个环
7.假设当前点为x,不断将栈顶弹出并记录,当栈顶与x相同,则表示该环已取出。
- 不难发现,根据上述流程,栈内应该是下图这样,弹出的顺序为D,C,B,但有向图是有方向的,显然顺序是反的,所以我们就要反向输出。
例题
题目大意:一个有向图有 N N N个顶点 N N N条边,输入 A [ i ] A[i] A[i],第 i i i条边是 i 到 A [ i ] i到A[i] i到A[i],找到一个没有重复点出现的循环,可以证明,存在解决方案。<