tarjan算法

tarjan算法在有向图求强连通分量
如果两个顶点间存在路径使得a可到达b b也可到达a,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通有向图的极大强连通子图,称为强连通分量(strongly connected components)
tarjan算法是基于dfs深度优先搜索的
算法中使用dfn和low数组 dfn数组来记录每个节点的访问时间 也就是访问的次序,强连通分量在dfs时所有的节点可以通过一次dfs访问完成 ,所以在dfs时强连通分量在dfs树中是属于同一颗dfs树的,用low来记录强连通分量在dfs树的根节点在dfs时的访问时间(访问次序),当我们每访问一个顶点时就把这个顶点压入栈中,有一点不可否认在dfs树中子节点总是在根节点之后访问的 ,当low=dfn时这个节点就为强连通分量在dfs树种的根节点 我们只要把栈里在这个节点之后访问的都退栈 就是和这个节
属于同一个强连通分量的节点 在程序中实现起来就是 :
访问节点a 时 节点b和节点a相连接 (1)如果b节点已经被访问过在堆栈中则 a节点的low[a]=min(low[a],dfn[b]) 因为这种情况是dfs时先访问了b 然后再访问a 然后再通过a可以访问b 也就是说有a->b 的路径 也有b->a的路径 他们属于同一个强连通分量 b 可能是a在dfs树中根节点 (2)如果b节点没有被访问过 则说明b不是a的根 low[a]=min(low[a],low[b])

这里写图片描述
在这个图中 我们从1结点开始 dfs 下面模拟下程序执行的过程
low[1]=dfn[1]=1;1 入栈
low[2]=dfn[2]=2;2入栈
low[3]=dfn[3]=3;3入栈
low[4]=dfn[4]=4;4入栈
low[5]=dfn[5]=5;5入栈
当执行到5号节点时发现他和1相连 而1已经在栈中 low[5]=min(low[5] dfn[1]) =1;
在递归返回过程中
low[4]=min(low[5],low[4])=1;
low[3]=min(low[3],low[4])=1;
low[2]=min(low[2],low[3])=1;
low[1]=min(low[1],low[2])=1;
检测到low[1]=dfn[1] 于是退栈直到退到1 位置(1也退栈) 形成一个强连通分量

在整个过程中每个节点访问一次 每个边访问一次 所以算法复杂度为0(m+n)

下面是代码

#include <iostream>
#include <stdio.h>
#include <stack>
#include <sstream>
using namespace std;
const int nmax=50;
stack<int> ms;//栈用来存放dfs中的访问信息
int istack[nmax];//顶点是否在栈中的标志
int vis[nmax];//dfs的访问标志位
int time;//tarjan中的时间计数
int low[nmax];//强联通分量在dfs搜索树中的根节点的访问时间
int dfn[nmax];//每个节点的访问时间
struct edge
{
    int adj_vex;
    struct edge *next;
};
struct vertex
{
    int vex;
    struct edge *first;
};
struct graph
{
    struct vertex vexs[nmax];
    int verxnum;
}mg;
void init()
{
    for(int i=0;i<nmax;i++)//清空dfs的访问标志 和顶点在栈中的访问标志
    {
        vis[i]=0;
        istack[i]=0;
        dfn[i]=0;
        low[i]=0;
    }
    time=0;
}
void readgraph()
{
    edge *temp;
    int adjvex;
    char a;
    string b;
    cout<<"please input the number of vertexs"<<endl;
    cin>>mg.verxnum;
    for(int i=1;i<=mg.verxnum;i++)//初始化连接表
    {
        mg.vexs[i].vex=i;
        mg.vexs[i].first=NULL;
    }
        getchar();
    for(int i=1;i<=mg.verxnum;i++)//输入每个顶点的边表信息
    {
        cout<<"please input the "<<i<<" vertex's edge "<<endl;
        getline(cin,b);
        istringstream mystream(b);
        while(mystream>>adjvex)
        {      
            temp=new edge;
            temp->adj_vex=adjvex;
            temp->next=mg.vexs[i].first;
            mg.vexs[i].first=temp;
        }

    }
}
void tarjan(int s)
{
    time++;
    low[s]=dfn[s]=time;
    vis[s]=1;
    istack[s]=1;
    ms.push(s);
    for(struct edge* e=mg.vexs[s].first;e;e=e->next )//依次访问与s相连的顶点
    {
        int j=e->adj_vex;
        if(!vis[j])//没有被访问过 不在堆栈中的点
        {
            tarjan(j);
            if(low[s]>low[j])
                low[s]=low[j];
        }
        else if(istack[j]&&low[s]>dfn[j])//在堆栈中的点 可能是s的根节点
            low[s]=dfn[j];
    }
    if(dfn[s]==low[s])//当搜索的点是 强连通分量在搜索树中的根节点时 退栈打印出强连通分量
    {
        cout<<" the strongly connected components are :"<<endl;
        int n;
        do
        {
            n=ms.top();
            ms.pop();
            istack[n]=0;
            cout<<n<<" ";
        }while(n!=s);
        cout<<endl;
    }
}
int main(int argc,char *argv[])
{
    readgraph();
    init();
    for(int i=1;i<=mg.verxnum;i++)
    {
        if(!vis[i])
        {
            tarjan(i);
        }
    }
    while(1);
    return  0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hebastast

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值