图论-tarjan

Tarjan算法

一个图是否连通可以通过tarjan算法搞定。

结点

public class vertexNode {
	/*
	 * 边的链式存储
	 * */
	public int v;		//起点
	public int next;	//终点
	
}

java实现算法,垃圾代码…

package alo_Graph;
public class Graph_Tarjan {
	
	static int cnt;
	static int tot;
	static int index,m=0,k=1;

	/******************************************tarjan算法-***********************************************/
	//比较两个数返回较小值
	static int min(int a,int b) {
		return a>b?b:a;
	}
	//边的插入操作
	static void add(G g,int x,int y){
		g.EG1[++cnt] = new vertexNode();
	    g.EG1[cnt].next=g.heads[x];
	    g.EG1[cnt].v = y;
	    g.heads[x]=cnt;
	}
	
	//代表第几个点在处理。递归的是点。
	static void tarjan(G g,int x){
	    g.DFN[x]=g.LOW[x]=++tot;	//新进点的初始化
	    g.stack[++index]=x;			//进站
	    g.visit[x]=1;				//表示在栈里
	    for(int i=g.heads[x];i!=-1;i=g.EG1[i].next){
	    	if(g.DFN[g.EG1[i].v]==0) {					//如果没访问过
	    		tarjan(g,g.EG1[i].v);					//往下进行延伸,开始递归
	            g.LOW[x]=min(g.LOW[x],g.LOW[g.EG1[i].v]);	//递归出来,比较谁是谁的儿子/父亲(树的对应关系)
	        }else if(g.visit[g.EG1[i].v]==1){  			//如果访问过,并且还在栈里。
	            g.LOW[x]=min(g.LOW[x],g.DFN[g.EG1[i].v]);	//比较谁是谁的儿子/父亲(链接对应关系)
	        }
	    }
	    //发现是整个强连通分量子树里的最小根。
	    if(g.LOW[x]==g.DFN[x]){
	    	do{
	    		System.out.print(g.stack[index]+"-");		//打印输出连通子图
	    		g.pr[m][g.stack[index]] = k;
	    		g.visit[g.stack[index]]=0;
	    		index--;
	    	}while(x!=g.stack[index+1]);//出栈,并且输出。
	        	g.number++;m++;k++;
	        System.out.println();
	    }
	}
	
	//打印tarjan算法结果 给出连通图的个数
	public static String print_tarjan(G g) {
		String temp = "";int z=1;		//记录连通子图数量
		if(g.number == 1) {
			temp+="<";
			for(int n1=0;n1<g.vernum;n1++) {
				temp+=g.vertexes[n1].data+" ";
			}
			temp+=">";
			//number为1表示只有1个连通区域
		}else {
			for(int n=0;n<g.number;n++) {
				temp+="<";
				for(int n1=0;n1<g.vernum;n1++){
					if(g.pr[n][n1]==z) {
						temp+=g.vertexes[n1].data+" ";
					}
				}
				z++;
				temp+=">\r\n";
			}
		}
		return temp;
	}
	
	//tarjan连通性算法的主方法 外部调用该方法实现图的强连通性判断 划分连通子图
	public static void tarjan_main(G g){
		cnt=0;
		tot=0;
		index=0;m=0;k=1;
		g.DFN = new int[g.MAX_N];	//初始化各个数组
		g.LOW = new int[g.MAX_N];
		g.stack = new int[g.MAX_N];
		g.heads = new int[g.MAX_N];
		g.pr = new int[10][g.MAX_N];
		g.visit = new int[g.MAX_N];
		int i;g.number=0;m=0;k=1;	//初始化各变量
		for(i=0;i<g.MAX_N;i++){
			g.heads[i] = -1;
			g.DFN[i] = 0;
			g.LOW[i] = 0;
			g.visit[i] = 0;
	    }
		for(i=0;i<10;i++) {		//初始化二维连通分布数组
			for(int j=0;j<g.MAX_N;j++){
			        g.pr[i][j] = 0;
			}
		}
		g.EG1 = null;
		g.EG1 = new vertexNode[g.MAX_N];//初始化图的边的集合
	    for(i=0;i<g.arcnum;i++){
	    	add(g,g.EG[i].u,g.EG[i].v);
	    	add(g,g.EG[i].v,g.EG[i].u);
	    }
	    for(i=1;i<g.vernum;i++)
	        if(g.DFN[i]==0)tarjan(g,i);//当这个点没有访问过,就从此点开始。防止图没走完
	}
	
}

大神C++版本

#include <iostream>
#include <string.h>
using namespace std;
const int MAX_N=10000;
struct edge
{
    int to,next;
}e[MAX_N];
int p[MAX_N],eid;
void init()
{
    eid=0;
    memset(p,-1,sizeof(p));
}
void insert(int u,int v)
{
    e[eid].to=v;
    e[eid].next=p[u];
    p[u]=eid++;
}
int dfn[MAX_N],low[MAX_N];     //当前时间戳,最早次序号,dfn只能初始化为0!
int idx=0;                                   //时间戳初始化为0
int belong[MAX_N],scc=0;         //belong记录每个顶点属于哪个强连通分量,scc为强连通分量总数
int s[MAX_N],top=0;                 //模拟栈
bool instack[MAX_N];              //是否在栈中
void tarjan(int u)
{
    dfn[u]=low[u]=++idx;
    s[top++]=u;
    instack[u]=true;
    for(int i=p[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u])
    {
        ++scc;
        do
        {
            belong[s[--top]]=scc;
            instack[s[top]]=false;
        }
        while(s[top]!=u);
    }
}
int main()
{
    init();
    int n,m;
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        insert(a,b);
    }
 //   tarjan(0);
    for(int i=1;i<=n;i++)          //用这种方法更新全部点
        if(dfn[i]==0)
             tarjan(i);                    
    for(int i=1;i<=n;i++)
        cout<<i<<"->"<<belong[i]<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值