图--求强连通分支 算法导论p338

 求强连通分支的基本步骤有:

1、建立原图连接表G和其转置图连接表T

2、对原图G进行深度搜索

3、对原图进行拓扑排序

4、对图再按原图G拓扑排序后的顺序对转置图T进行深搜

5、输出强连通分支

#include<stdio.h>
#include<stdlib.h>

typedef struct node
{
 int adjvex;
 struct node *next;
}EdgeNode;

typedef struct vnode
{
 char c;
 int dis;;//节点的发现时间
 int fish;//节点的完成时间
 int pre;//节点深搜时的前驱
 int color;//1代表白色表示该点尚未发现 2代表灰色现已发现 3代表黑色表示与它邻接的边已发现
 EdgeNode *firstedge;
}VertexNode;

typedef struct
{
	int dis;//用于标记AdjList中的 dis
	int id;  //用于标记AdjList向量的序号
}point;

int time;//时间计数器

void InPut(VertexNode G[],VertexNode T[],int n,int e)  //节点及边的录入 以及建立图G邻接表及它的转置T邻接表
{  
	int i,j,s,d,find;
    char ch1,ch2;
	EdgeNode *p,*q;
	printf("请输入一次输入所有字符节点 且第一个输入的点默认为源节点\n");
	for(i=0;i<n;i++)
	{
		scanf("%c ",&G[i].c);
		T[i].c=G[i].c;
		G[i].firstedge=T[i].firstedge=NULL;
	}
	printf("请输入各条边\n");
	for(i=1;i<=e;i++)//建立邻接表 由于是无向图则要申请两个节点p和q
	{   
		find=0;
		scanf("%c %c ",&ch1,&ch2);
		for(j=0;find!=2&&j<n;j++) 
			if(ch1==G[j].c)
			{
				s=j;
				find++;         
			}
			else if(ch2==G[j].c)
			{
				d=j;
				find++;   
			}
			p=(EdgeNode*)malloc(sizeof(EdgeNode));//用于原图G连接表的建立
			p->adjvex=d;   
			p->next=G[s].firstedge;  
			G[s].firstedge=p;
			
			q=(EdgeNode*)malloc(sizeof(EdgeNode));//用于原图G的转置T连接表的建立
			q->adjvex=s;
			q->next=T[d].firstedge;
			T[d].firstedge=q;
	}
}

void DFS_VISIT(VertexNode AdjList[],int u)//深度搜索递归
{  
	int v;
	EdgeNode *p;
	AdjList[u].color=2;//u节点已发现 标记为灰色
	time++;
	AdjList[u].dis=time;
	p=AdjList[u].firstedge;
	while(p)
	{
		v=p->adjvex;
		if(AdjList[v].color==1)
		{
			AdjList[v].pre=u;
			DFS_VISIT(AdjList,v);
		}
		p=p->next;
	}  
	AdjList[u].color=3;//u节点所有邻接点均已发现,即完成任务,标记为黑色
	AdjList[u].fish=time=time+1;//标记完成时间 
}

void DFS(VertexNode AdjList[],int n)//深度搜索
{    
	int i;
	for(i=0;i<n;i++)
	{
		AdjList[i].color=1;//1代表白色表示该点尚未发现
		AdjList[i].pre=-1;//前驱向量初始化
	}	
	time=0;
	for(i=0;i<n;i++)
		if(AdjList[i].color==1)
			DFS_VISIT(AdjList,i);   
}

void printpath(VertexNode AdjList[],int i,int n)//递归输出源点到各点的路径
{  
	if(AdjList[i].pre==-1)
	{
		printf("%c->",AdjList[i].c);
		AdjList[i].color=4;
		return ;
	}
	else
	{
		printpath(AdjList,AdjList[i].pre,n);
		if(i==n)
			printf("%c",AdjList[i].c);
		else			
			printf("%c->",AdjList[i].c);		
		AdjList[i].color=4;
	}
}

int cmpdis(const void *a,const void *b)//用于节点发现时间由大到小排列
{
	return(  ((point*)b)->dis-((point*)a)->dis  );//
}

void print(VertexNode AdjList[],int n)输出源点到各点的长度及路径
{ 
	int i,j,id,count;
	point p[10001];	
	for(i=0;i<n;i++)
	{
		p[i].dis=AdjList[i].dis;
		p[i].id=i;
	}
	qsort(p,n,sizeof(p[0]),cmpdis);//时间按发现时间由大到小排序  利于T连接表的强连通量的深搜
	count=0;//记录强连通分支的个数
	for(i=0;i<n;i++)
	{   
		id=p[i].id;//发现时间较晚的节点 在T中的位置
		if(AdjList[id].pre!=-1&&AdjList[id].color==3)
		{  
			printf("强连通分支%d: ",++count);
			for(j=0;j<n;j++)
				AdjList[j].color=3;
			printpath(AdjList,id,id);//递归输出源点到各点的路径
			printf("\n");
		}
		else if(AdjList[id].pre==-1&&AdjList[id].color==3)
		{
			printf("强连通分支%d: ",++count);
			printf("%c\n",AdjList[id].c);
		}
	}
}

int cmpfish(const void *a,const void *b)
{
	return(  ((VertexNode*)b)->fish-((VertexNode*)a)->fish  );//从大到小排列
}

void TOPOLOGICAL_SORT(VertexNode AdjList[],int n)//拓扑排序
{
//	int i; 
	qsort(AdjList,n,sizeof(AdjList[0]),cmpfish);//时间按完成时间由大到小排序
/*	for(i=0;i<n;i++)//排序输出
		printf("%3c ",AdjList[i].c);
	printf("\n");
	for(i=0;i<n;i++)
		printf("%3d ",AdjList[i].fish);
	printf("\n");   */
}

void DFS2(VertexNode T[],int n)
{
	int i;
	qsort(T,n,sizeof(T[0]),cmpfish);//时间按完成时间由大到小排序
	for(i=0;i<n;i++)
	{
		T[i].color=1;//1代表白色表示该点尚未发现
		T[i].pre=-1;//前驱向量初始化
	}	
	time=0;	
	for(i=0;i<n;i++)
		if(T[i].color==1)
			DFS_VISIT(T,i);	
		print(T,n);	
}

int main()
{  
	freopen("1.txt","r",stdin);
	int n,e;
	int i;
	VertexNode G[10001];//原连接表图
	VertexNode T[10001];//原连接图的转置
	printf("请输入字符节点的个数N 和边的数目E:\n");
	scanf("%d%d ",&n,&e);
	InPut(G,T,n,e); //节点及边的录入 以及建立邻接表	
	DFS(G,n);// 深度搜索1
//	print(G,n);	
	for(i=0;i<n;i++)
		T[i].fish=G[i].fish;
	TOPOLOGICAL_SORT(G,n);//拓扑排序 	
	DFS2(T,n);//拓扑排序后再对图G的转置图T进行深搜
		return 0;
}



 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gdliweibing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值