图的强连通分量计算

本文介绍了两种寻找图中强连通分量(SCC)的经典算法:Kosaraju算法和Tarjan算法。Kosaraju算法通过两次深度优先搜索实现,而Tarjan算法则是一种线性时间复杂度的方法。文章提供了详细的算法实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Kosaraju算法:算法分两次dfs,第一次dfs会将所有的强连通分量(SCC)混淆在一起,按SCC图拓扑排序的逆序进行遍历即可将所有SCC分开。

#include<stdio.h> //tre_fei_ge
#include<string.h>
int stack[100],top;
int G2[12][12],G[12][12] = {{0,1,0,0,0,0,0,0,0,0,0,0},
				 {0,0,1,1,1,0,0,0,0,0,0,0},
				 {0,0,0,0,0,1,0,0,0,0,0,0},
				 {0,0,0,0,0,0,0,0,0,0,0,0},
				 {0,1,0,0,0,1,1,0,0,0,0,0},
				 {0,0,1,0,0,0,0,1,0,0,0,0},
				 {0,0,0,0,0,0,0,1,0,1,0,0},
				 {0,0,0,0,0,0,0,0,0,0,1,0},
				 {0,0,0,0,0,0,1,0,0,0,0,0},
				 {0,0,0,0,0,0,0,0,1,0,0,0},
				 {0,0,0,0,0,0,0,0,0,0,0,1},
				 {0,0,0,0,0,0,0,0,0,1,0,0}};//G2为图G的转置矩阵
int vis[12],sccno[12],scc_cnt;//vis访问标记数组,sccno点所在SCC标记数组
void dfs1(int u);
void dfs2(int u);
void find();
int main()
{
	for(int j = 0;j < 12;j++)
		for(int k = 0;k < 12;k++)
			G2[k][j] = G[j][k];
	memset(stack,-1,sizeof(stack));
	top = 0;
	memset(vis,0,sizeof(vis));
	memset(sccno,0,sizeof(sccno));
	scc_cnt = 0;
	for(int i = 0; i < 12; i++) {
		for(int j = 0; j < 12; j++)
			printf("%d ",G[i][j]);
		printf("\n");
	}
	printf("\n================================\n\n");
	for(int i = 0; i < 12; i++) {
		for(int j = 0; j < 12; j++)
			printf("%d ",G2[i][j]);
		printf("\n");
	}
	find();
	for(int i = 0; i < 12; i++)
		printf("%d\t",sccno[i]);
	printf("\n");
	for(int i = 0; i < 12; i++)
		printf("%d\t",i);
	return 0;
 } 
void dfs1(int u) {
	if(vis[u])	return ;
	vis[u] = 1;
	for(int i = 0; i < 12; i++)
		if(G[u][i])
			dfs1(i);
	stack[top++] = u;
}
void dfs2(int u) {
	if(sccno[u]) return ;
	sccno[u] = scc_cnt;
	for(int i = 0; i < 12; i++)
		if(G2[u][i])
			dfs2(i);
}
void find() {
	for(int i = 0; i < 12; i++)	dfs1(i);
	for(int i = 11; i >= 0; i--) {
		if(!sccno[stack[i]]) {
			scc_cnt++;
			dfs2(stack[i]);
		}
	}
}

Tarjan算法:SCC的第一个线性算法,即有w->v->u,若v的后代能访问的最早的祖先为v及v是v所在的SCC的第一个被访问的点,若为w则w应为v所在的SCC的第一个被访问到的点,若为u则应先计算出u所在SCC的所有点的集合。

#include<stdio.h>
#include<string.h>
#define inited -1
int stack[100],top;
int G[12][12] = {{0,1,0,0,0,0,0,0,0,0,0,0},
				 {0,0,1,1,1,0,0,0,0,0,0,0},
				 {0,0,0,0,0,1,0,0,0,0,0,0},
				 {0,0,0,0,0,0,0,0,0,0,0,0},
				 {0,1,0,0,0,1,1,0,0,0,0,0},
				 {0,0,1,0,0,0,0,1,0,0,0,0},
				 {0,0,0,0,0,0,0,1,0,1,0,0},
				 {0,0,0,0,0,0,0,0,0,0,1,0},
				 {0,0,0,0,0,0,1,0,0,0,0,0},
				 {0,0,0,0,0,0,0,0,1,0,0,0},
				 {0,0,0,0,0,0,0,0,0,0,0,1},
				 {0,0,0,0,0,0,0,0,0,1,0,0}};
int pre[12],sccno[12],lowlink[12],scc_cnt,dfs_clock;//pre当前点访问时间标记数组,lowlink当前点所能到的最早祖先访问时间标记数组
void dfs(int u);
void init();
int min(int a,int b);
int main()
{
	init();
	for(int i = 0; i < 12; i++)
		dfs(i);
	for(int i = 0; i < 12; i++)
		printf("%d\t",i);
	printf("\n");
	for(int i = 0; i < 12; i++)
		printf("%d\t",sccno[i]);
	return 0;
}
void init() {
	memset(stack,-1,sizeof(stack));
	top = -1;
	memset(pre,-1,sizeof(pre));
	memset(sccno,-1,sizeof(sccno));
	memset(lowlink,-1,sizeof(lowlink));
	scc_cnt = 0;
	dfs_clock = 0;
	for(int i = 0; i < 12; i++)
		dfs(i);
}
void dfs(int u) {
	pre[u] = lowlink[u] = ++dfs_clock;
	stack[++top] = u;
	for(int i = 0; i < 12; i++)
		if(G[u][i] && pre[i] == inited) {
			dfs(i);
			lowlink[u] = min(lowlink[u],lowlink[i]);
		} else if(G[u][i]){
			lowlink[u] = min(lowlink[u],pre[i]);
		}
	printf("DFS %d %d\n",lowlink[u],pre[u]);
	if(lowlink[u] == pre[u] && sccno[u] == inited) {
		printf("======cul======\n");
		scc_cnt++;
		while(top >= 0) {
			sccno[stack[top]] = scc_cnt;
			if(u == stack[top--]) break;
		}
	}
}
int min(int a,int b) {
	return a > b ? b : a;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值