tarjan算法理解

1,在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected component)。

2,下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。

3,Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
定义几个关键数组:
int DFN[MAX]; //记录节点u第一次被访问时的步数
int LOW[MAX]; //记录与节点u和u的子树节点中最早的步数

接下来是对算法流程的演示。


从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。

返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。


返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1。



继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以LOW[2]=DFN[4]=5。返回1后,发现DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量{1,3,4,2}。


运行Tarjan算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(N+M)。

    #include<iostream>  
    #include<vector>  
    using namespace std;  
      
    const int MAX=10001;  
      
    int Stop;//栈中的元素个数  
    int cnt;//记录连通分量的个数  
    int visitNum;//记录遍历的步数  
    int DFN[MAX]; //记录节点u第一次被访问时的步数  
    int LOW[MAX]; //记录与节点u和u的子树节点中最早的步数  
    bool instack[MAX];//记录节点u是否在栈中  
    int Stap[MAX];//栈  
    int Belong[MAX];//记录每个节点属于的强连通分量编号  
      
    int N;//节点个数  
      
    vector<int> tree[MAX];  
      
    void tarjan(int i)  
    {  
        int j;  
        DFN[i]=LOW[i]=++visitNum;  
        instack[i]=true;  
        Stap[++Stop]=i;//将当前节点压入栈中  
        for (unsigned k=0;k<tree[i].size();k++)  
        {  
            j=tree[i][k];  
            if (!DFN[j]) //j还没有被访问过  
            {  
                tarjan(j);  
                //父节点是子节点的子节点  
                if (LOW[j]<LOW[i])  
                    LOW[i]=LOW[j];  
            }  
            //与j相连,但是j已经被访问过,且还在栈中  
            //用子树节点更新节点第一次出现的时间  
            else if (instack[j] && DFN[j]<LOW[i])  
                LOW[i]=DFN[j];  
        }  
        //节点i是强连通分量的根  
        if (DFN[i]==LOW[i])  
        {  
            cnt++;  
            //输出找到的强连通分量  
            cout<<"连通分量"<<cnt<<": ";  
            //退栈,直至找到根为止  
            do  
            {  
                j=Stap[Stop--];  
                instack[j]=false;  
                cout<<j<<" ";  
                Belong[j]=cnt;  
            }  
            while (j!=i);  
            cout<<endl;  
        }  
    }  
    void solve()  
    {  
        Stop=cnt=visitNum=0;  
        memset(DFN,0,sizeof(DFN));  
        for (int i=1;i<=N;i++)  
            if (!DFN[i])//有可能图不是连通图  
                tarjan(i);  
    }  
      
    int main()  
    {  
        N=6;  
        tree[1].push_back(3);  
        tree[1].push_back(2);  
        tree[2].push_back(4);  
        tree[3].push_back(5);  
        tree[3].push_back(4);  
        tree[4].push_back(1);  
        tree[4].push_back(6);  
        tree[5].push_back(6);  
        solve();  
        for(int i=1;i<=N;i++)  
            cout<<Belong[i]<<" ";  
        cout<<endl;  
        return 0;  
    }  


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值