有向图的强连通分量是指:在强连通分量中的每一对顶点都存在一条路径可达,比如对于顶点对v和u,在强连通分量中v到u可达,那么u到v也可达。比如在左图中v1、v2、v3是一个强连通分量,v2也是一个强连通分量。强连通分量右图所示:
![](https://img-blog.csdn.net/20151016103236896)
![](https://img-blog.csdn.net/20151016103911922)
深度优先搜索是求有向图的强连通分量的有效方法。(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)。