zoukankan      html  css  js  c++  java
  • Tarjan算法---强联通分量

    1、基础知识

          在有向图G,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。  下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。


          Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈。如果发现某节点u有边连到搜索树中栈里的节点v,则更新u的low 值为dfn[v](更新为low[v]也可以)。如果一个节点u已经DFS访问结束,而且此时其low值等于dfn值,则说明u可达的所有节点,都不能到达任何在u之前被DFS访问的节点 那么该节点u就是一个强连通分量在DFS搜索树中的根。此时将栈中所有节点弹出,包括u,就找到了一个强连通分量。

    定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。

      Low(u)=MIN{ DFN(u),  Low(v),(u,v)为树枝边,u为v的父节点,DFN(v),(u,v)为指向栈中节点的后向边(非横叉边) }

      当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。

    2、参考代码

      1 /* Tarjan算法:求一个有向图G=(V,E)里极大强连通分量。
      2   强连通分量是指有向图G里顶点间能互相到达的子图。
      3   如果一个强连通分量已经没有被其它强通分量完全包含,那这个强连通分量就是极大强连通分量*/
      4 /*low[v]=dfn[v]时,栈里v以及v以上的顶点全部出栈,一个极大强连通分量*/
      5 #include<stdio.h>
      6 #include<stdlib.h>
      7 #define MAXL 50
      8 #define MIN(x,y) ((x) < (y) ? (x) : (y))
      9 
     10 
     11 //结点定义
     12 typedef struct edge_node{
     13     int key;
     14     struct edge_node *next;
     15 }ENode;
     16 typedef struct{
     17     char vertex;
     18     ENode *firstedge;
     19 }VNode;
     20 typedef VNode VList[MAXL];
     21 typedef struct{
     22     VList vlist;
     23     int n,e;
     24 }ALGraph;
     25 int instack[MAXL];  //用于标记是否在栈中
     26 int vis[MAXL];
     27 int dfn[MAXL],low[MAXL];
     28 int depth;
     29 
     30 int ind[MAXL]={0};
     31 
     32 int top;
     33 int stack[MAXL];   //【【【要用到】】】
     34 int num_scc;
     35 
     36 int count_SCCele;
     37 int scc[MAXL];
     38 ALGraph *ALG=(ALGraph *)malloc(sizeof(ALGraph));
     39 
     40 //邻接表生成[有向图]
     41 void creat_ALGraph(ALGraph *ALG)
     42 {
     43     int i,j,k;
     44     char ch1,ch2;
     45     ENode *ep;
     46     
     47     scanf("%d,%d",&ALG->n,&ALG->e);
     48     for(i=0;i<ALG->n;i++) //顶点表
     49     {
     50         getchar();
     51         scanf("%c",&ALG->vlist[i].vertex);
     52         ALG->vlist[i].firstedge=NULL;
     53     }
     54     for(k=0;k<ALG->e;k++)  //边表
     55     {
     56         getchar();
     57         scanf("%c,%c",&ch1,&ch2);
     58         for(i=0;ALG->vlist[i].vertex!=ch1;i++);
     59         for(j=0;ALG->vlist[j].vertex!=ch2;j++);
     60 
     61         ep=(ENode*)malloc(sizeof(ENode));
     62         ep->key=j;
     63         ep->next=ALG->vlist[i].firstedge;
     64         ALG->vlist[i].firstedge=ep;
     65     }
     66 }
     67 
     68 //邻接表输出
     69 void print_ALGraph(ALGraph *ALG)
     70 {
     71     int i;
     72     ENode *ptr=(ENode*)malloc(sizeof(ENode));
     73     for(i=0;i<ALG->n;i++)
     74     {
     75         printf("%c",ALG->vlist[i].vertex);
     76         ptr=ALG->vlist[i].firstedge;
     77         while(ptr!=NULL)  //不能用!ptr
     78         {
     79             printf("->%c",ALG->vlist[ptr->key].vertex);
     80             ptr=ptr->next;
     81         }
     82         printf("
    ");
     83     }
     84 }
     85 
     86 //计算初始化用于dfnlow()和bicon()
     87 void init_Tarjan(void)
     88 {
     89     depth=0;
     90     for(int i=0;i<ALG->n;i++)
     91     {
     92         instack[i]=0;
     93         dfn[i]=low[i]=-1;
     94         vis[i]=0;
     95     }
     96 
     97     top=0;  //栈初始化
     98     for(int j=0;j<ALG->n;j++)
     99         stack[j]=-1;
    100     num_scc=0;
    101 }
    102 
    103 void init_scc(void)  //scc块初始化
    104 {
    105     count_SCCele=0;
    106     for(int i=0;i<ALG->n;i++)
    107         scc[i]=-1;
    108 }
    109 
    110 void SCC_Tarjan(int u)
    111 {
    112     int son;
    113     ENode *ptr=(ENode *)malloc(sizeof(ENode));
    114 
    115     dfn[u]=low[u]=depth++;  //访问+访问标记+入栈+入栈标记+遍历
    116     instack[u]=1;
    117     vis[u]=1;
    118     stack[top++]=u;
    119     ptr=ALG->vlist[u].firstedge;
    120     while(ptr!=NULL)
    121     {
    122         son=ptr->key;
    123         if(!vis[son])
    124         {
    125             SCC_Tarjan(son);
    126             low[u]=MIN(low[u],low[son]);
    127         }
    128         else if(instack[son])  //在栈中
    129         {
    130             low[u]=MIN(low[u],dfn[son]);
    131         }
    132         ptr=ptr->next;
    133     }
    134     if(dfn[u] == low[u])   //若此,以u为根的强连通分量
    135     {
    136         num_scc++;
    137         init_scc();
    138         do{
    139             top--;
    140             scc[count_SCCele++]=stack[top];
    141             instack[stack[top]]=0;
    142         }while(stack[top] != u);
    143 
    144         for(int cn=0;cn<count_SCCele;cn++)
    145             printf("%c ",ALG->vlist[scc[cn]].vertex);
    146         printf("
    ");
    147     }
    148 }
    149 
    150 int main(void)
    151 {
    152     creat_ALGraph(ALG);
    153     print_ALGraph(ALG);
    154 
    155     init_Tarjan();
    156     for(int i=0;i<ALG->n;i++)   //***可以处理不连通的图,如果连通只需要一次即可,即给定一个root,直接bridge_Tarjan(root,-1)***
    157         if(!vis[i])
    158             SCC_Tarjan(i);
    159 //    SCC_Tarjan(2);  //root可以自定义
    160 
    161     printf("%d
    ",num_scc);
    162 
    163     printf("%s %s %s
    ","ver","dfn","low");
    164     for(int l=0;l<ALG->n;l++)
    165         printf("%c: %3d %3d
    ",ALG->vlist[l].vertex,dfn[l],low[l]);
    166 
    167     return 0;
    168 }
    ---  纵使山重水复,亦会柳暗花明   sunqh1991@163.com   欢迎关注,互相交流
  • 相关阅读:
    程序猿身边有个漂亮女程序媛~~~那是种什么样的体验?
    前端程序猿分九段,一段又一段,你是哪一段?
    10个屌炸天的设计网址导航带你嗨翻科技设计界 #精选前端开发设计素材
    人工智能一定高大上?盘点那些让人哭笑不得的人工智障 #精选黑科技人工智能
    javascript奇技淫巧之位运算符
    曾经的超级明星类库jQuery未来也许不再会被前端程序猿追捧了!
    谷歌为什么把上十亿行代码都放在一个仓库里
    全功能响应式模板:黑暗元素
    程序员的福音,AI可以自动修复bug了!
    机器学习原来如此有趣:如何故意欺骗神经网络
  • 原文地址:https://www.cnblogs.com/wjcx-sqh/p/5929929.html
Copyright © 2011-2022 走看看