求强连通分量几种算法的比较

来源:互联网 发布:淘宝运送方式怎么设置 编辑:程序博客网 时间:2024/05/29 14:51
对于求有向图的强连通分量 , 我常用的一般只有这三种算法Kosaraju、Tarjan、Garbow,而这三种算法 ,Tarjan和Garbow算法的思想是一样的 , 只不过是实现的方法不一样 , 下面就逐一分析这三个算法:

1、Kosaraju
   Kosaraju算法主要是利用有向图中原图和其反图之间的关系, 对于一个原图中的强连通分量 , 其在反图中还是强连通分量 , 原图中存在u 到 v的边 , 在反图中就只存在 v 到 u的边 ,而不存在u 到 v的边。 根据这个关系 , 我们先用原图进行dfs搜索 , 得到了一个森林(树) ,我们再按照原图中得到的dfs搜索序列 , 在其反图中进行dfs搜索 , 每次搜索能遍历到的顶点就是一个强连通分量。

代码:
//Kosaraju算法

int dfsone(int cur)
{
    temp[cur] =true;
    for(int i =head[cur] ; i != -1; i = edge[i].next)
    {
      if(!temp[edge[i].t])  dfsone(edge[i].t);
    }
    num[++sig] =cur;
    return0;
}

int dfstwo(int cur , int sig)
{
    temp[cur] =true;
    scc[cur] =sig;
    for(int i =head2[cur] ; i != -1; i = edge[i].next2)
    {
      if(!temp(edge[i].f))
         dfstwo(edge[i].f , sig);
    }
    return0;
}

int Kosaraju()
{
    int sig =0;
    memset(temp, 0 , sizeof(temp));
    for(int i =1; i <=n ; i++)
    {
      if(!temp[i])
          dfsone(i ,sig);
    }

    sig =0;
    memset(temp, 0 , sizeof(temp));

    for(int i =n; i > 0; i--)
    {
      if(!temp[num[i]])
       {
         dfstwo(num[i] , sig++);
       }
    }

    returnsig;
}

2、Tarjan算法
   Tarjan是利用跟求割点和桥的算法的思想差不多 ,都是利用dfs + 时间戳 。
   如果对原图进行dfs搜索 , 那么我们有强连通分量的定义可知, 任何一个强连通分量是原图的dfs搜索的子树 , 那么我们只要确定了强连通分量在dfs序列中的根 , 我们就能确定这个强连通分量 。在进行dfs时 , 我们用两个数组tem 、 low来记录每个点的时间戳 , low记录每个点的访问时间 ,tem记录所有子孙的最小low值 ,tem的初始值和low一样 。 所以在dfs搜索的回溯过程中 , 如果low[v] ==tem[v] , 我们我们就能确定一个强连通分量。

代码:
//tarjan算法
void dfs(int u)
{
   node.push(u);
    low[u] =tem[u] = ++dfs_clock;
    int i;
    for(i =head[i] ; i != -1; i = edge[i].next)
    {
       v =edge[i].to;
      if(!pre[v])
       {
          pre[v] =1;
          int lowv = =dfs(v);
          tem[u] =min(lowv , tem[u]);
       }
       elseif(!sccno[i])  tem[u] = min(tem[u] ,tem[v]);//tem是记录每个点的子孙所能到达的low最小的点
    }

    if(low[u] ==tem[u])
    {
      scc_clock++;
       do
       {
          int x =node.top(); node.pop();
          sccno[x] =scc_clock;
       }while(x !=u)
    }
}

3、Garbow算法
  Garbow算法和Tarjan算法的另一种实现Tarjan算法是用了tem数组来求强连通分量的根 ,而Carbow算法是利用一个stack来求强连通分量的根。
代码:
//carbow算法

void dfs(int u)
{
   node1.push(u) , node2.push(u);
    low[u] =++dfs_clock;
    int i ,v;
    for(i =head[i] ; i != -1; i = edge[i].next)
    {
       v =edge[i].to;
      if(!pre[v])
       {
          pre[v] =1;
         dfs(v);
       }
       elseif(!sccno[v]) //这里就表示已经存在一个环了 , node2就把这个环中除点v之外所有点都删除
       {
         while(low[node2.top()] > low[v])
            node2.pop();
       }
    }

   if(node2.top() == u)
    {
      scc_clock++;
      node2.pop();
       do
       {
          x =node1.top();
         node1.pop();
          sccno[x] =scc_clock;
       }while(x !=u)
    }
}

三种算法的比较:
这三种算法由于都是用的邻接表 , 因此时间复杂度都为:O(n+m) , 但是Carbow算法的常数最小 , Tarjan次之 ,并且Carbow算法的是实现非常精妙。
0 0
原创粉丝点击