强连通分量

有向图的强连通分量是指:在强连通分量中的每一对顶点都存在一条路径可达,比如对于顶点对v和u,在强连通分量中v到u可达,那么u到v也可达。比如在左图中v1、v2、v3是一个强连通分量,v2也是一个强连通分量。强连通分量右图所示:
强连通分量_第1张图片强连通分量_第2张图片
    
    
    
    
深度优先搜索是求有向图的强连通分量的有效方法。
(1)在有向图中,我们从某个顶点进行深度优先搜索,并将所有邻接点搜索完成的逆序存储在数组arr中。
(2)再次对有向图的逆图,进行深度优先搜索,此时,按照存储在arr数组中的顺序搜索逆图。每完成一次DFS就是原图中的一个强连通分量。知道所有顶点都搜索完成,我们就可以知道强连通分量的个数。
比如:在下图中,我们从v1进行深度优先遍历,得到的arr数组为{v1,v3,v4,v2};则再次从v1出发深度优先遍历逆图,得到两个顶点集{v1,v3,v4}和{v2}。这就是有向图的两个强连通分量。
#include<iostream>
#include<vector>
#include<fstream>
#include<time.h>
using namespace std;

void readGraph();//读取文件,存储图
void readGraph();//读取文件,存储逆图
void DFSTraverse();//正向遍历图
void DFSRTraverse();//正向遍历逆图
void DFS(int v);//从顶点v开始遍历图
void DFSR(int v);//从顶点v开遍历逆向图

int nodeNum;//图中顶点数
int edgeNum;//图中边数
vector<vector<int>> mGraph;//图的存储结构
vector<vector<int>> mRGraph;//存储逆向图
int *indegree;//存储顶点的入度数
bool *visited;//访问数组
int *arr;//记录退出DFS函数之前完成搜索的顶点编号
int num;


//从文件中读取数据,存储在图中
void readGraph()
{
	fstream fin("E:\\myData\\soc-Epinions.txt");
	fin>>nodeNum>>edgeNum;
	mGraph.resize(nodeNum);
	indegree = new int[nodeNum]();
	visited = new bool[nodeNum]();
	arr = new int[nodeNum]();
	num = nodeNum - 1;
	int num1, num2;
	while(fin>>num1>>num2)
	{
		mGraph[num1].push_back(num2);
		indegree[num2]++;
	}
	fin.close();
}

//读取文件存储逆向图
void readRGraph()
{
	fstream fin("E:\\myData\\soc-Epinions.txt");
	fin>>nodeNum>>edgeNum;
	mRGraph.resize(nodeNum);
	for(int i = 0; i < nodeNum; ++i)
	{
		int val = indegree[i];
		mRGraph[i].reserve(val);
	}
	int num1, num2;
	while(fin>>num1>>num2)
	{
		mRGraph[num2].push_back(num1);
	}
	fin.close();
}

//遍历正向图
void DFSTraverse()
{
	for(int i = 0; i < nodeNum; ++i)
	{
		if(!visited[i])
		{
			DFS(i);
		}
	}
}

void DFS(int v)
{
	visited[v] = true;
	int count = mGraph[v].size();
	for(int i = 0; i < count; ++i)
	{
		int val = mGraph[v][i];
		if(!visited[val])
		{
			DFS(val);
		}
	}
	arr[num--] = v;
}

void DFSRTraverse()
{
	int count = 0;
	for(int i = 0; i < nodeNum; ++i)
	{
		visited[i] = false;
	}
	for(int i = 0; i < nodeNum; ++i)
	{
		if(!visited[arr[i]])
		{
			DFSR(arr[i]);
			count++;
		}
	}
	cout<<"强连通分量的个数"<<count<<endl;
}

void DFSR(int v)
{
	visited[v] = true;
	int count = mRGraph[v].size();
	for(int i = 0; i < count; ++i)
	{
		int val = mRGraph[v][i];
		if(!visited[val])
		{
			DFSR(val);
		}
	}
}

int main(void)
{
	readGraph();
	readRGraph();
	cout<<"图中顶点的个数:"<<nodeNum<<endl;
	cout<<"图中边的条数:"<<edgeNum<<endl;
	clock_t start, finish;
	start = clock(); 
	DFSTraverse();
	DFSRTraverse();
	finish = clock(); 
	cout<<"求强连通分量的时间:"<<(double)(finish - start) / CLOCKS_PER_SEC * 1000<<endl;

	system("pause");
	return 0;
}

实验结果:单位(ms)
数据集为soc-Epinions
图中顶点的个数:75888
图中边的条数:508837
强连通分量的个数42185
求强连通分量的时间:15
数据集为Email-EuAll
图中顶点的个数:265214
图中边的条数:420045
强连通分量的个数231000
求强连通分量的时间:31

总结:时间复杂度和深度优先遍历图的时间复杂度相同:o(n + e)。

 
 
 

你可能感兴趣的:(强连通分量)