实验代码(github)
一、实验内容
- 这次实验是在上次实验的基础上完成的,也就是说他们存储图的方式是相同的,所以下面的报告内容我会着重解释拓扑排序的具体实现,以及强连通图的实现。
- 实验结果测试用图:
- 期望得到的结果:
- 拓扑排序: 1->5->2->4->3
- 强连通图:(1,5,2) (3) (4)
- 期望得到的结果:
二、理论准备
1、拓扑排序
- 拓扑排序:对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。
- 实现方法及思路:首先进行DFS,并记录下每个节点设置为black的time时刻。然后按照完成时间从大到小对节点进行排序即可。
2、强连通图
- 强连通图:强连通图(Strongly Connected Graph)是指在有向图G中,如果对于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,则称G是强连通图。
- 实现思路:
- 首先进行DFS并记录下每个节点的访问完成时间。
- 然后把图进行转置(所有边反向)。
- 然后在从完成时间最大(最后完成)的顶点再次开始DFS,每完成一个子树的DFS就输出它访问了的顶点。每次都选完成时间最大的那个顶点开始DFS,直到所有的顶点都被访问。
- 每次的输出就是一个强连通子图。
三、实验环境
- 操作系统及版本:windows10
- 编译软件及版本:g++6.3.0
- 使用的计算机语言:c++语言
四、实验过程
1、拓扑排序
- 在真正实现的时候我把DFS和拓扑排序同时实现了,每次在DFS访问结束一个顶点以后就把这个加入到拓扑排序中来。实现代码如下:
//toplolgical sort
Vertex* vertexp = new Vertex(vertexPtr->getVertexId());
if(topologicalSort == NULL)
{
this->topologicalSort = vertexp;
}
else
{
vertexp->setNextVertex(topologicalSort);
this->topologicalSort = vertexp;
}
第一句就是用DFS刚刚访问完的那个节点Id新建一个vertex,然后判断是否已经有顶点加到topological中,如果没有那么这个顶点就是加进去的第一个。如果有,先让这个顶点的next指针指向原先的topol链表,在把topo指针指向vertexp。这样一来每个新加进来的vertex都会排在前面,刚好就是按照完成时间从大到小的排列。
2、强连通图
- 按照上面说的思路实现了,具体代码如下:
cout<<"SCC results: \n";
dfs(startVertex_in);
exchangePointerForGT();
Vertex* temp = GT;//这里是原来的图
//进行转置
while(temp != NULL)
{
Node *nodePtr = temp->getHeadNode();
while(nodePtr != NULL)
{
addEdge(nodePtr->getVertex()->getVertexId(),temp->getVertexId());
nodePtr = nodePtr->getNextNode();
}
temp = temp->getNextVertex();
}
temp = topologicalSort;//原来的图不用了,直接扔了
int time = 0;
while(temp != NULL)
{
Vertex* vertexPtr = this->headVertex;
while(vertexPtr != NULL)//查找拓扑排序最先并且没有访问过的节点,进行访问
{
if(vertexPtr->getVertexId() == temp->getVertexId() && vertexPtr->getColor() == white)
{
dfsSub(vertexPtr, time);
cout<<endl;//每次访问完一个子树就会打出一个换行,表示一个强连通子图
break;
}
vertexPtr = vertexPtr->getNextVertex();
}
temp = temp->getNextVertex();
}
exchangePointerForGT();
- 首先进行DFS,然后函数
exchangePointerForGT();
把刚刚DFS的图放在另一个私有变量GT中,然后在根据这个图,新建一个转置后的图。 temp = topologicalSort;
这句保存了拓扑排序的结果,防止对新建的进行DFS时破坏了原先的拓扑排序。- 然后就开始对新的图的DFS,这里每次完成一个dfssub后都要再次查找剩下的节点中拓扑排序最靠前的节点。
五、实验结果
- 由于在求强连通子图的时候会用到DFS的结果,所以输出中也会包含DFS的结果。
然后后面的1->2->5表示他们是一个强连通子图。4->是一个强连通子图,3->是一个强连通子图。
六、实验总结
- 这次由于是邻接链表来存储图,所以进行转置时代价很高,需要再次重建一个图。
- 并且SCC部分代码破坏性很强,进行过SCC以后这个图就不能在用了,因为这个很乱的,如果在用在其它代码中,往后的维护工作将时非常艰难的。