有向无环图(DAG图)就是没有环路的有向图,即以任意一个顶点为起点出发,走任意一条路径也不能回到起点。
有向无环图常用来描述一项工程的进行过程,这个工程分为一系列子工程,而一些子工程的开始必须以某些子工程的结束为条件。
对应于一个工程或系统,人们常常关心两个问题:(1)工程能否顺利进行,这是拓扑排序问题。(2)工程完成所需要的最短时间,这是关键路径问题。
拓扑排序
由某个集合上的一个偏序,得到该集合上的一个全序。
偏序:R是集合上的二元关系,R满足(1)自反性(2)反对称性(3)传递性
全序:对于集合上的二元关系,满足(1)完全性(2)反对称性(3)传递性(完全性包含了自反性)
直观理解:偏序是集合中只有部分元素间可比较(先后、大小等),全序是集合中任意元素间都可比较。
顶点表示活动,弧表示活动间先后关系的有向图称为AOV网(顶点表示活动的网)。
拓扑排序步骤:重复以下步骤:(1)在有向图中选取一个没有前驱的顶点,输出之。(2)图中删除该点和所有与之相连的弧。直至图中不再有顶点(活动)存在。
我们用邻接表作为图的存储结构,稍有的一点改变是在顶点节点增加一个数据域,用来存储顶点入度。同时要使用一个栈来存储入度为零的顶点。邻接表和栈的实现用前面的代码。以下是拓扑排序代码实现:
int TopologicalSort(ALgraph *G)
{
SqStack s;
InitStack(s);
int indegree[G.vexnum-1]=0; //设置辅助数组,实时记录每个顶点的入度
FindInDegree(G,indegree); //辅助数组初始化,统计初始图中每个顶点的入度
for(int i = 0;i<G.vexnum-1;i++)
if(!indegree[i]) Push(s,i); //入度为0的顶点的序号入栈
int count = 0; //设一个计数变量,用来判断该图是否有环
while(s.top != s.base) //栈不为空
{
int i;
Pop(s,i);
printf("按拓扑排序顺序输出顶点,接下来的顶点是:%c",G.vertices[i].data);
count++; //每输出一个顶点,计数加一
for(ArcNode p = G.vertices[i].firstarc; p; p=p->nextarc)//与第i个顶点相连的所有弧
{
int j = p->adjvex; //弧指向的顶点
if(!(--indegree[j])) //入度减一
Push(s,j); //若入度为0,入栈
}
}
if(count<G.vexnum)
return ERROR; //说明该有向图有环
else
return OK;
}
void FindInDegree(ALGraph G,int* indegree)
{
for(int i = 0;i<G.vexnum-1;i++)
{
for(ArcNode p = G.vertices[i].firstarc; p; p=p->nextarc)//与第i个顶点相连的所有弧
{
int j = p->adjvex; //弧指向的顶点
indegree[j]++;
}
}
}
关键路径
弧表示活动,权值表示活动持续时间,顶点表示时间节点的有向图称为AOE网(边表示活动的网)
在AOE网中,有些活动可以并行的进行,所以完成工程的最短时间是由开始点到结束点的最长路径长度(路径上各活动持续时间之和),这条路径就叫关键路径。