一 Tarjan算法简介
1 定义
Targain算法本质上是一种DFS,通过对图的深度优先遍历得到图的强连通分量,在算法中我们使用一个DFN数组和一个LOW数组,DFN数组是储存这个点被搜索到的次序,而LOW数组表示的是这个点所能指向的点的最小值,如果一个图是环,那么咱们搜索完一周回溯到这个点的时候自然的会有LOW[i] = DFN[i],借助这个性质,我们再开一个栈去存储我们得到的点,不断出栈直到遇见我们最初进栈的那个起点。
2 实现
我们借助LOW数组和DFN数组,DFN数组存储的是每个点的DFS序,而LOW数组是这个点的子节点所能指向的最小DFN值
3. Code
int num;
int Index;
int Cnum;
// Cnum is the number of the strongly connected
int Belong[N];
void tarjan(int x)
{
stack[++num] = x;
DFN[x] = LOW[x] = ++Index;
for(int i = head[i];i;i = E[i].next)
{
int to = E[i].to;
if(!DFN[to])
{
tarjan(to);
if(LOW[to] < LOW[x]) LOW[x] = LOW[to];
}
// if this place hasn't been visited, we tatjan it
if(!Instack[E[i].to]&&DFN[to] > DFN[x]) LOW[x] = DFN[to];
// if this palce isn't in the stack and it serial number is much bigger than it
}
// if this point is the first one begin visited, this elem above it is the strong connectivity
if(DFN[x]==LOW[x])
{
Cnum++;
int j = 0;
do
{
j = stack[num--];
Instack[j] = 0;
Belong[j] = j;
}
while (j!=x);
// we give the same connected the same number
}
}
二 利用tarjan求割点
1.定义
割点:在一个连通的图中,如果删除一个点之后,这个图变得是不连通的,那么这个点就是割点。
2 求解割点的方法
对于根节点而言,如果这个点是根节点且有两个或以上子树,那么这个点就是割点,因为删除这个点之后,这个多个子树上的点就不能相互到达,这个图变为不连通的,故为割点。
对于非根节点而言,如果这个点的子节点不能到达它的根节点,那么这个点删除之后这个图也变得是不连通的,所以这个点是割点
3 code
void tarjan(int x,int father)
{
LOW[x] = DFN[x] = Index++;
// initlize the node message
int child = 0;
for(int i = head[x];i;i = E[i].next)
{
int to = E[i].to;
if(!DFN[to])
{
tarjan(to,x);
// initlize the child node
LOW[x] = min(LOW[x], LOW[to]);
if(LOW[to] >= DFN[x]&&x!=father) ans[++ansNum] = x;
if(x==father) child++;
// if this node is the root of the graph
}
else LOW[x] = min(LOW[to], LOW[x]);
// upgrade the LOW of the node
}
if(child>=2&&x==father) ans[++ansNum] = father;
}
三 求解割边的方法
1 定义
对于一条边,如果删除它使得这个两个点之间变得不是连通,那么这个边就是割边
2 求解割边的方法
对于一个点而说,如果它的子节点不能回到父节点,那么这个边就是一条割边,删除这个边后,这个子节点就和这个父节点不再连通。借助DFN数组和LOW数组进行实现,如果LOW[childnode] < DFN[fathernode],则这个边是一条割边
3 code
void tarjan(int now,int father)
{
dfn[now] = low[now] = ++Index;
// printf("%d %d\n",dfn[now],low[now]);
int vis = 0;
for(int i = head[now]; i; i = E[i].next)
{
int to = E[i].to;
if(dfn[to])
{
if(to == father&&!vis) vis = 1;
// if this to os the father of it, we mark it for the first time
else low[now] = min(low[now], dfn[to]);
}
else
{
// if this node hasn't been dfs, then we dfs it
tarjan(to, now);
if(dfn[now] < low[to])
{
// if the point's dfn is smaller than the to node
Ans[++AnsNum].from = now;
Ans[AnsNum].to = to;
}
low[now] = min(low[now], low[to]);
}
}
}