关于对Tarjan算法的思考与尝试

Tarjan

–关于对Tarjan算法的思考与尝试

接下来(超级)详细的解释一下代码吧!

全局变量部分的解释
int i,j,n,m;
bool b[1001];
int color[1001],colorn;
int stack[1001],top;//数组模拟栈 
int s,e,temp,index;
int dfn[1001],low[1001];
int head[1001];
struct data
{
    int v;
    data *nxt;
}a[100001];

i,j:变量。
n:点数。
m:边数。
b[]:在有向图中某个点是否经历过(布尔)。
stack[]:数组模拟栈的操作(其实就是一个数组啦~)
top:模拟栈顶,通过top–来模拟pop操作。
color[]:表示这是第几个已确定的强连通分量。
colorn:是一个累加器,记录已经确定的强连通分量个数,便于为强连通分量编号。
s,e,temp:变量。
index:累加器,是一个时间戳,来记录遍历过的节点数。
DFN[ i ] : 在DFS中该节点被搜索的次序(时间戳)
LOW[ i ] : 为i或i的子树能够追溯到的最早的栈中节点的次序号
当DFN[ i ]==LOW[ i ]时,为i或i的子树可以构成一个强连通分量。
struct内部的东西就是图论的基本功啦,如果不会请移步至链表入门

读入优化
int r()
{
    char ch=getchar();
    int ans=0;
    while(ch<'0'||ch>'9')
    {
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        ans*=10;
        ans+=ch-'0';
        ch=getchar();
    }
    return ans;
}
插入节点

这也是基本功。如果不会可以借鉴解释 in SPFA入门。在这里不再赘述。

int ins(int ss,int ee)
{
    temp++;
    a[temp].nxt=&a[head[ss]];
    head[ss]=temp;
    a[temp].v=ee;
}

Tarjan算法

核心在这里↓↓↓

void tarjan(int x)
{
    dfn[x]=++index;
    low[x]=index;
    b[x]=1;
    stack[++top]=x;
    data *p=&a[head[x]];
    while(p->v!=0)
    {
        int vv=p->v;
        if(!dfn[vv])
        {
            tarjan(vv);
            low[x]=min(low[x],low[vv]);
        }
        else if(b[vv])
        {
            low[x]=min(low[x],dfn[vv]);
        }
        p=p->nxt;
    }
    if(dfn[x]==low[x])
    {
    b[x]=0;
    color[x]=++colorn;
    while(stack[top]!=x)
    {
        color[stack[top]]=colorn;
        b[stack[top--]]=0;
    }
    top--;
    }
}

下面详细解释:
tarjan算法首先是有一个类似于DFS的过程,如果不会DFS,那就……呃~~(╯﹏╰)……就请关闭此页。
首先从1号点开始深度优先搜索,每搜一个他的DFN[]=经历过的点数,即++index,LOW[]为栈中最早的与其相连的点的次序号。
当搜到某个点恰好没有出度了,就停止搜索,开始判断,如果DFN==LOW那么他自己就是一个强联通分量。
以此类推往前回退,如果对应点的下一条边不是NULL,就往下一条路走,而且不能走已经被访问过的节点。
然后如果在队列里再一次搜到栈中已有的元素,就使LOW=MIN(LOW[],DFN[])。
如果对于一个出发点x,他的终点之一vv没有被访问过,就继续深搜,这个递推过程完成后,LOW[x]=min(LOW[x],LOW[vv]),来让他们的根节点尽量小,这是我们人为设定的标准。当然了根节点向数字大的方向规定也行。

主函数

包括输入输出过程等。

int main()
{
    n=r(),m=r();
    for(i=1;i<=m;i++)
    {
        s=r(),e=r();
        ins(s,e);
    }
    for(i=1;i<=n;i++)
    if(!col[i])
    tarjan(i);
    for(i=1;i<=colorn;i++)
    {
        for(j=1;j<=n;j++)
        if(color[j]==i)
        cout<<j<<" ";
        cout<<endl;
    }
}

Tarjan的简要步骤(蒟蒻的总结)
1. 建立一个有向图,用链表存
2. 以1为起始点往后深搜至第一次搜
到头为止,每搜一次DFN时间戳与LOW
+1。
3. 如果DFN[]=LOW[],就是一个根节
点,那么他就是一个强连通分量。
4. 以此类推往前回退,如果对应点
的下一个点不是NULL,就往下一条路
走,而且不能走已经被访问过的节点
。然后如果在队列里再一次搜到下一
元素就使MIN(LOW[],DFN[])。 、
最后给大家推荐一个网页,大家可以通过例子更好的理解:
http://www.cnblogs.com/uncle-lu/p/5876729.html
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值