Tarjan算法

一 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]);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值