有向图的强连通分量算法之Tarjan算法

9 篇文章 0 订阅
8 篇文章 0 订阅
//有向图的强连通分量:利用深度优先搜索树求有向图的强连通分量
//------Tarjan算法:------\\
//(1)设置DFN和LOW两个数组,其中DFN用于按搜索顺序给各个结点盖时间戳,LOW用于表示当前结点所能到达的最前的结点(即DFN/LOW最小)
//(2)按深度优先搜索方法遍历,并更新各结点LOW值;
//(3)最后判断DFN和LOW值是否相等,相等则该点为后续结点能到达的最前的结点、能形成的最大的环
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define MAX_VERTEX_NUM 20
typedef int Status;
#define OK 1
#define ERROR 0
typedef enum {FALSE,TRUE} Boolean;
typedef int InfoType;  //定义弧相关信息为整形
typedef char VertexType;    //定义顶点类型为字符型
typedef char ElemType;

//十字链表表示的图结构
typedef struct ArcNode
{
    int tailvex,headvex;               //该弧所指向的顶点在顺序结构中的位置
    struct ArcNode *hlink,*tlink;  //分别为与该弧头邻接(同一尾结点)的下一条弧的指针和与该弧尾邻接(同一尾结点)的下一条弧的指针
    InfoType *info;           //该弧相关信息的指针
}ArcNode;                     //弧结构
typedef struct VNode
{
    VertexType data;            //顶点
    ArcNode *firstin,*firstout;          //分别为该顶点的第一条入弧和第一条出弧
}VNode,OrtList[MAX_VERTEX_NUM]; //顶点结构
typedef struct
{
    OrtList vertices;     //顶点数组
    int vexnum,arcnum;    //顶点数和弧数
}OLGraph;                 //十字链表表示的图结构

//栈结构和方法
typedef struct
{
    int st[MAX_VERTEX_NUM];
    int top;
}Stack;
void StackPush(Stack *S,int u);
void StackPop(Stack *S,int *v);

Status CreateDG(OLGraph *G);
int LocateVex(OLGraph G,VertexType v);

void SCCs(OLGraph *G);
Boolean InStack[MAX_VERTEX_NUM];
int DFN[MAX_VERTEX_NUM];
int LOW[MAX_VERTEX_NUM];
void Tarjan(int u,Stack *S,int *index,int *SCCcount,OLGraph *G);
int min(int v,int u);

int main()
{
    OLGraph *G;
    G=(OLGraph *)malloc(sizeof(OLGraph));

    CreateDG(G);
    int i;
    //打印十字链表
    printf("打印十字链表表示的图结构:\n");
    for(i=0;i<G->vexnum;i++)
    {
        ArcNode *p;
        p=(ArcNode *)malloc(sizeof(ArcNode));
        p=G->vertices[i].firstout;
        printf("%c ",G->vertices[i].data);
        while(p)
        {
            int t;
            t=p->headvex;

            printf("%c ",G->vertices[t].data);
            p=p->tlink;
        }
        printf("\n");
    }

    printf("通过深度优先遍历求强连通分量:\n");
    SCCs(G);

    return 0;
}
int LocateVex(OLGraph G,VertexType v)
{
    int i;
    for(i=0;i<G.vexnum;i++)
        if(G.vertices[i].data==v)
            return i;
    return -1;
}
//创建十字链表表示的有向图
Status CreateDG(OLGraph *G)
{
    int i,IncInfo;
    printf("输入顶点数量:");
    scanf("%d",&G->vexnum);
    printf("输入弧数量:");
    scanf("%d",&G->arcnum);
    printf("弧是否含有其他信息(否---0,是---1):");
    scanf("%d",&IncInfo);
    //头结点处理
    for(i=0;i<G->vexnum;i++)
    {
        fflush(stdin);
        printf("输入第%d个顶点:",i+1);
        scanf("%c",&G->vertices[i].data);    //输入头结点的顶点信息
        G->vertices[i].firstin=G->vertices[i].firstout=NULL;        //初始化头结点的出、入弧指针
    }
    //弧结点处理
    for(i=0;i<G->arcnum;i++)
    {
        char v1,v2;
        int headvex,tailvex;
        fflush(stdin);
        printf("输入第%d条弧的头结点:",i+1);
        scanf("%c",&v1);
        fflush(stdin);
        printf("输入第%d条弧的尾结点:",i+1);
        scanf("%c",&v2);

        headvex=LocateVex(*G,v1);
        tailvex=LocateVex(*G,v2);

        ArcNode *p;
        p=(ArcNode *)malloc(sizeof(ArcNode));

        p->headvex=headvex;
        p->tailvex=tailvex;
        //对弧结点的hlink和tlink应用头插法:即将头结点的firstin放在当前弧的hlink后面,再把当前弧放在头结点的firstin后面
        p->hlink=G->vertices[headvex].firstin;
        G->vertices[headvex].firstin=p;
        p->tlink=G->vertices[tailvex].firstout;
        G->vertices[tailvex].firstout=p;
        if(IncInfo)
        {
            int info;
            printf("输入弧的其他信息:");
            scanf("%d",&info);
            p->info=&info;
        }
        else
            p->info=NULL;
    }
    return OK;
}

void SCCs(OLGraph *G)
{
    int i,index,SCCcount;  //index用于随着深度优先搜索的顺序给各结点盖时间戳,
    index=SCCcount=0;      //SSCcount用于计算强连通分量的个数
    Stack S;               //S栈用于按搜索顺序存储各结点
    S.top=0;
    //数组初始化
    for(i=0;i<G->vexnum;i++)
    {
        DFN[i]=LOW[i]=0;
        InStack[i]=FALSE;
    }
    //和深度优先搜索一样,先从一个结点沿接邻接点往下搜索,当最后一个结点无邻接点或其邻接点都已访问时
    //则往上回溯,看上一个结点的其余邻接点,直至第一个结点,再从另一个结点开始。
    for(i=0;i<G->vexnum;i++)
        if(!DFN[i])          //利用DFN[i]数组是否为0来判断该顶点是否被访问过
        {
            Tarjan(i,&S,&index,&SCCcount,G);
        }
}
//---Tarjan算法---//
void Tarjan(int u,Stack *S,int *index,int *SCCcount,OLGraph *G)
{
    int v;
    DFN[u]=LOW[u]=++(*index);
    StackPush(S,u);           //将访问的结点入栈
    InStack[u]=TRUE;          //标记该结点在栈中

    ArcNode *p;
    p=(ArcNode *)malloc(sizeof(ArcNode));

    for(p=G->vertices[u].firstout;p;p=p->tlink)  //当回溯时,遍历当前结点其他的邻接点,
    {
        int v;
        v=p->headvex;

        if(!DFN[v])    //DFN[v]为0,则表示未被访问过
        {
            Tarjan(v,S,index,SCCcount,G); //如果邻接点未被访问过,继续往下
            LOW[u]=min(LOW[u],LOW[v]);   //从深度优先搜索回溯时,更新当前结点的LOW值
        }
        else if(InStack[v])
        {
            LOW[u]=min(LOW[u],LOW[v]);  //如果当前结点已经在栈中,
        }
    }
    //如果DFN值和LOW值相等,则表明回溯到了根结点,即强连通分量的起点
    if(DFN[u]==LOW[u])
    {
        int t;
        printf("第%d个强连通分量:",++(*SCCcount));
        do
        {
            //将从强连通分量的起点开始的结点到栈顶的结点全部退栈、打印
            StackPop(S,&t);
            InStack[t]=FALSE;
            printf("%c ",G->vertices[t].data);
        }while(t!=u);
        printf("\n");
    }
}
void StackPush(Stack *S,int u)
{
    S->st[S->top++]=u;
}
void StackPop(Stack *S,int *v)
{
    *v=S->st[--S->top];
}
int min(int v,int u)
{
    return v<=u ? v:u;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值