//有向图的强连通分量:利用深度优先搜索树求有向图的强连通分量
//------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;
}
有向图的强连通分量算法之Tarjan算法
最新推荐文章于 2022-09-07 10:08:31 发布