强连通分量Kosaraju算法

参考阅读:https://blog.csdn.net/lhl1124281072/article/details/79699870
如何理解Kosaraju算法? - 简致的回答 - 知乎
https://www.zhihu.com/question/58926821/answer/583402591
Kosaraju算法步骤:

  1. 用DFS后序遍历原图的反转图,得到后序遍历顺序
  2. 用得到的后序遍历顺序对原图DFS
    代码实现
    栈和图的定义
const int MaxV = 100;

/*定义栈*/
typedef struct SNode{
	string *Data;
	int Top;
	int MaxSize;
} * Stack;

/*定义边*/
typedef struct ENode{
	int v1, v2;
	string va, vb;
} * Edge;

/*定义邻接点*/
struct AdjVNode{
	int AdjV;	//顶点int型编号
	AdjVNode *Next;
};

/*定义邻接表头*/
typedef struct VNode{
	AdjVNode *EdgeFirst;
	string Data;
	bool tag;
} AdjList[MaxV];

/*定义图*/
typedef struct GNode{
	int Nv, Ne;
	AdjList L;
} * LGraph;

对栈需要的操作

/*创造一个新的栈*/
Stack CreateStack(int MaxSize)
{
	Stack S = new SNode;
	S->Data = new string[MaxSize];
	S->MaxSize = MaxSize;
	S->Top = -1;
	return S;
}

/*将x压入栈*/
void Push(Stack S, string x)
{
	S->Data[++S->Top] = x;
}

/*返回元素x是否在栈中位置, 若不在返回-1*/
int FindS(Stack S, string x)
{
	for (int i = S->Top; i >= 0; i--)
		if(S->Data[i] == x)
			return i;
	return -1;
}

/*将边由string型加入对应的int型顺序编号*/
void Edge_Code(Edge E, Stack S)
{
	int k = FindS(S, E->va);
	if(k < 0)	//如果顶点不在栈中, 压入栈
	{
		Push(S, E->va);
		E->v1 = S->Top;	//得到新的v1的编号
	}
	else
		E->v1 = k;		//得到已有v1的编号
	k = FindS(S, E->vb);
	if (k < 0) //如果顶点不在栈中, 压入栈
	{
		Push(S, E->vb);
		E->v2 = S->Top; //得到新的v2的编号
	}
	else
		E->v2 = k; //得到已有v2的编号
}

图的操作

/*创造新的无边图*/
LGraph CreateGraph(int Nv)
{
	LGraph G = new GNode();
	G->Nv = Nv;
	return G;
}

/*边的插入*/
void InsertEdge(LGraph G, Edge E)
{
	AdjVNode *V = new AdjVNode;
	V->AdjV = E->v2;
	V->Next = G->L[E->v1].EdgeFirst;
	G->L[E->v1].EdgeFirst = V;
}

/*键入建立图*/
LGraph BuildGraph()
{
	int Nv;
	cin >> Nv;
	LGraph G = CreateGraph(Nv);
	cin >> G->Ne;
	Edge E = new ENode;
	Stack S = CreateStack(G->Nv);
	//使用栈来储存顶点数据,每输入一个顶点, 判断之前有没有输入过, 没有就压入栈返回栈顶下标, 有就返回在栈中的元素的位置下标
	for (int i = 0; i < G->Ne; i++)
	{
		cin >> E->va >> E->vb;
		Edge_Code(E, S);	//由string得到int编号
		InsertEdge(G, E);	//插入边
		G->L[E->v1].Data = E->va;	//顶点存的数据为字符编号
		G->L[E->v2].Data = E->vb;
	}
	delete E;
	delete[] S->Data;
	delete S;
	return G;
}

/*删除图*/
void DeleteGraph(LGraph G)
{
	AdjVNode *V;
	for (int i = 0; i < G->Nv; i++)
	{
		while (G->L[i].EdgeFirst)
		{
			V = G->L[i].EdgeFirst;
			G->L[i].EdgeFirst = V->Next;
			delete V;
		}
	}
	delete G;
}

输出图的物理结构

/*遍历输出图的邻接表图的物理结构*/
void ShowGraph(LGraph G)
{
	AdjVNode *V;
	for (int i = 0; i < G->Nv; i++)
	{
		V = G->L[i].EdgeFirst;
		cout << G->L[i].Data << "->";
		while (V)
		{
			cout << G->L[V->AdjV].Data << "->";
			V = V->Next;
		}
		cout << "NULL" << endl;
	}
}

得到图G的倒置图Gr

/*返回图G的倒置图Gr*/
LGraph ReverseGraph(LGraph G)
{
	LGraph Gr = CreateGraph(G->Nv);
	Edge E = new ENode;
	AdjVNode *V;
	for (int i = 0; i < G->Nv; i++)
	{
		Gr->L[i].Data = G->L[i].Data;	//先复制顶点的string数据
		V = G->L[i].EdgeFirst;
		E->v2 = i;
		while (V)
		{
			E->v1 = V->AdjV;
			InsertEdge(Gr, E);
			V = V->Next;
		}
	}
	delete E;
	return Gr;
}

Kosaraju算法

/*找出字符x在图G中的编号*/
int FindG(LGraph G, string x)
{
	for (int i = 0; i < G->Nv; i++)
		if (G->L[i].Data == x)
			return i;
	return -1;
}
void DFS(LGraph G, int u, Stack S)
{
	G->L[u].tag = true;
	AdjVNode *V = G->L[u].EdgeFirst;
	while (V)
	{
		if(!G->L[V->AdjV].tag)
			DFS(G, V->AdjV, S);
		V = V->Next;
	}
	Push(S, G->L[u].Data);
}

void Kosaraju(LGraph G)
{
	Stack S;
	int *reverse = new int[G->Nv];
	/*这里得到Gr图, 并输出原图和倒置图的物理结构*/
	LGraph Gr = ReverseGraph(G);
	cout << "原图: " << endl;
	ShowGraph(G);
	cout << "倒置图" << endl;
	ShowGraph(Gr);
	S = CreateStack(Gr->Nv);		//建立新栈, 用于储存DFS后序遍历顺序

	for (int i = 0; i < Gr->Nv; i++)	//对Gr进行DFS得出后序遍历,标记一下
		if(!Gr->L[i].tag)
			DFS(Gr, i, S);

	for (int i = 0; i < Gr->Nv; i++)
		reverse[i] = FindG(Gr, S->Data[i]);

	delete[] S->Data;
	delete S;

	int start = 0;
	S = CreateStack(G->Nv);
	cout << "该图包含以下强连通分量:" << endl;
	for (int i = G->Nv - 1; i >= 0; i--)
	{
		if (!G->L[reverse[i]].tag)	//对每个顶点DFS,都会把该顶点所在的强连通分量遍历完才退出DFS
		{
			DFS(G, reverse[i], S);
			for (int i = start; i <= S->Top; i++)
				cout << S->Data[i] << " ";
			cout << endl;
			//cout << S->Data[S->Top] << endl;
			start = S->Top + 1;
		}
	}
	DeleteGraph(G);
	DeleteGraph(Gr);
	delete[] reverse;
	delete[] S->Data;
	delete S;
}

程序运行
输入
在这里插入图片描述

13 22
0 1
0 5
2 0
2 3
3 2
3 5
4 2
4 3
5 4
6 0
6 4
6 9
7 6
7 8
8 7
8 9
9 10
9 11
10 12
11 4
11 12
12 9

运行结果
在这里插入图片描述
完整代码

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MaxV = 100;

/*定义栈*/
typedef struct SNode{
	string *Data;
	int Top;
	int MaxSize;
} * Stack;

/*定义边*/
typedef struct ENode{
	int v1, v2;
	string va, vb;
} * Edge;

/*定义邻接点*/
struct AdjVNode{
	int AdjV;	//顶点int型编号
	AdjVNode *Next;
};

/*定义邻接表头*/
typedef struct VNode{
	AdjVNode *EdgeFirst;
	string Data;
	bool tag;
} AdjList[MaxV];

/*定义图*/
typedef struct GNode{
	int Nv, Ne;
	AdjList L;
} * LGraph;

/*创造一个新的栈*/
Stack CreateStack(int MaxSize)
{
	Stack S = new SNode;
	S->Data = new string[MaxSize];
	S->MaxSize = MaxSize;
	S->Top = -1;
	return S;
}

/*将x压入栈*/
void Push(Stack S, string x)
{
	S->Data[++S->Top] = x;
}

/*返回元素x是否在栈中位置, 若不在返回-1*/
int FindS(Stack S, string x)
{
	for (int i = S->Top; i >= 0; i--)
		if(S->Data[i] == x)
			return i;
	return -1;
}

/*将边由string型加入对应的int型顺序编号*/
void Edge_Code(Edge E, Stack S)
{
	int k = FindS(S, E->va);
	if(k < 0)	//如果顶点不在栈中, 压入栈
	{
		Push(S, E->va);
		E->v1 = S->Top;	//得到新的v1的编号
	}
	else
		E->v1 = k;		//得到已有v1的编号
	k = FindS(S, E->vb);
	if (k < 0) //如果顶点不在栈中, 压入栈
	{
		Push(S, E->vb);
		E->v2 = S->Top; //得到新的v2的编号
	}
	else
		E->v2 = k; //得到已有v2的编号
}

/*创造新的无边图*/
LGraph CreateGraph(int Nv)
{
	LGraph G = new GNode();
	G->Nv = Nv;
	return G;
}

/*边的插入*/
void InsertEdge(LGraph G, Edge E)
{
	AdjVNode *V = new AdjVNode;
	V->AdjV = E->v2;
	V->Next = G->L[E->v1].EdgeFirst;
	G->L[E->v1].EdgeFirst = V;
}

/*键入建立图*/
LGraph BuildGraph()
{
	int Nv;
	cin >> Nv;
	LGraph G = CreateGraph(Nv);
	cin >> G->Ne;
	Edge E = new ENode;
	Stack S = CreateStack(G->Nv);
	//使用栈来储存顶点数据,每输入一个顶点, 判断之前有没有输入过, 没有就压入栈返回栈顶下标, 有就返回在栈中的元素的位置下标
	for (int i = 0; i < G->Ne; i++)
	{
		cin >> E->va >> E->vb;
		Edge_Code(E, S);	//由string得到int编号
		InsertEdge(G, E);	//插入边
		G->L[E->v1].Data = E->va;	//顶点存的数据为字符编号
		G->L[E->v2].Data = E->vb;
	}
	delete E;
	delete[] S->Data;
	delete S;
	return G;
}

/*遍历输出图的邻接表图的物理结构*/
void ShowGraph(LGraph G)
{
	AdjVNode *V;
	for (int i = 0; i < G->Nv; i++)
	{
		V = G->L[i].EdgeFirst;
		cout << G->L[i].Data << "->";
		while (V)
		{
			cout << G->L[V->AdjV].Data << "->";
			V = V->Next;
		}
		cout << "NULL" << endl;
	}
}

/*删除图*/
void DeleteGraph(LGraph G)
{
	AdjVNode *V;
	for (int i = 0; i < G->Nv; i++)
	{
		while (G->L[i].EdgeFirst)
		{
			V = G->L[i].EdgeFirst;
			G->L[i].EdgeFirst = V->Next;
			delete V;
		}
	}
	delete G;
}

/*返回图G的倒置图Gr*/
LGraph ReverseGraph(LGraph G)
{
	LGraph Gr = CreateGraph(G->Nv);
	Edge E = new ENode;
	AdjVNode *V;
	for (int i = 0; i < G->Nv; i++)
	{
		Gr->L[i].Data = G->L[i].Data;	//先复制顶点的string数据
		V = G->L[i].EdgeFirst;
		E->v2 = i;
		while (V)
		{
			E->v1 = V->AdjV;
			InsertEdge(Gr, E);
			V = V->Next;
		}
	}
	delete E;
	return Gr;
}

/*找出字符x在图G中的编号*/
int FindG(LGraph G, string x)
{
	for (int i = 0; i < G->Nv; i++)
		if (G->L[i].Data == x)
			return i;
	return -1;
}
void DFS(LGraph G, int u, Stack S)
{
	G->L[u].tag = true;
	AdjVNode *V = G->L[u].EdgeFirst;
	while (V)
	{
		if(!G->L[V->AdjV].tag)
			DFS(G, V->AdjV, S);
		V = V->Next;
	}
	Push(S, G->L[u].Data);
}

void Kosaraju(LGraph G)
{
	Stack S;
	int *reverse = new int[G->Nv];
	/*这里得到Gr图, 并输出原图和倒置图的物理结构*/
	LGraph Gr = ReverseGraph(G);
	cout << "原图: " << endl;
	ShowGraph(G);
	cout << "倒置图" << endl;
	ShowGraph(Gr);
	S = CreateStack(Gr->Nv);		//建立新栈, 用于储存DFS后序遍历顺序

	for (int i = 0; i < Gr->Nv; i++)	//对Gr进行DFS得出后序遍历,标记一下
		if(!Gr->L[i].tag)
			DFS(Gr, i, S);

	for (int i = 0; i < Gr->Nv; i++)
		reverse[i] = FindG(Gr, S->Data[i]);

	delete[] S->Data;
	delete S;

	int start = 0;
	S = CreateStack(G->Nv);
	cout << "改图包含以下强连通分量:" << endl;
	for (int i = G->Nv - 1; i >= 0; i--)
	{
		if (!G->L[reverse[i]].tag)	//对每个顶点DFS,都会把该顶点所在的强连通分量遍历完才退出DFS
		{
			DFS(G, reverse[i], S);
			for (int i = start; i <= S->Top; i++)
				cout << S->Data[i] << " ";
			cout << endl;
			//cout << S->Data[S->Top] << endl;
			start = S->Top + 1;
		}
	}
	delete[] reverse;
	delete[] S->Data;
	delete S;
}

int main()
{
	LGraph G = BuildGraph();
	Kosaraju(G);

	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值