拓扑排序
拓扑排序是将有向无环图的每个顶点排成一个线性序列。
经典的例子就是AOV网大学四年课程规划的例子。
AOV网
在现代化管理中,人们常用有向图来描述和分析一项工程的计划和实施过程,一个工程常被分为多个小的子工程,这些子工程被称为活动(Activity),在有向图中若以顶点表示活动,有向边表示活动之间的先后关系,这样的图简称为AOV网。
以计算机软件工程专业为例,把该专业的大学四年课程放在一个图中。这些课之间有的有先后关系,有的没有先后关系。比如C语言和高等数学没有先后关系,而离散数学必须在数据结构之前,等等。其实,软件工程专业的四年的所有课程就形成了一个无环有向图。对这个无环有向图进行拓扑排序,形成一个线性序列,然后根据每学期排课的门数,把这个线性序列截成6段,这些课程 就分成了6个学期的课程。这就是拓扑排序!
算法步骤:
1.从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
2.从图中删除该顶点和所有以它为起点的有向边。
3.重复 1 和 2 直到当前的 DAG 图为空。(如果最后不为空,即存在无前驱的顶点,则说明该图是有环图)
这是拓扑排序的一个实例(对于一个图,拓扑排序后的结果不一定唯一)
下面程序的输入输出样例是基于上面的这个图进行的。
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1000;
int indegree[maxn];
int mp[maxn][maxn];
string str="0abcdefgh";
void topology_sort(int mp[maxn][maxn],int indegree[maxn],int n){
//最多搞n次,如果n次后输出的结点个数小于n,则存在环,本程序假设无环
for(int i=1;i<=n;i++){
//对入度为0的点进行操作
for(int j=1;j<=n;j++){
if(indegree[j]==0){
cout<<str[j]<<" ";
indegree[j]--;
//删除与该结点为起点的边
for(int k=1;k<=n;k++){
if(mp[j][k]==1) indegree[k]--;
}
break;
}
}
}
cout<<endl;
}
int main()
{
memset(indegree,0,sizeof(indegree));
cout<<"输入一个图的邻接矩阵: "<<endl;
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++){
cin>>mp[i][j];
if(mp[i][j]==1) indegree[j]++;
}
cout<<"拓扑排序的一个结果: ";
topology_sort(mp,indegree,8);
return 0;
}
/*
输入输出样例:
输入一个图的邻接矩阵:
0 0 1 0 0 0 1 0
0 0 0 0 0 0 1 1
0 0 0 1 0 0 0 0
0 0 0 0 1 0 1 0
0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 1 0 0
拓扑排序的一个结果: a b c d g h f e
*/
关键路径
AOE网
AOE网:Activity on edge network
若在带权的有向图中,以顶点表示事件,以有向边表示活动,边上的权值表示活动的开销(如该活动持续的时间),则此带权的有向图称为AOE网。
AOE网具有以下两个性质:
①只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始。
②只有在进入一某顶点的各有向边所代表的活动都已经结束,该顶点所代表的事件才能发生。
AOE网中的关键路径,就是完成整个网络所需的最短时间,亦最长路径。