目录
有向无环图(DAG)是指一个无环路的有向图。有向无环图可用来描述工程或系统的进行过程,如一个工程的施工图、学生课程间的制约关系图等。
一、什么是AOV网?
AOV网:在一个表示工程的有向无环图中,用顶点表示活动,用弧表示活动间的优先关系,称这
样的图为顶点表示活动的网(Activity On Vertex Network),简称为AOV网。
注意:
- AOV网中的弧表示活动之间存在的某种制约关系;
- AOV网中不能出现回路,因为自己不能作为自己的先决条件。
下面给出一个AOV网:计算机专业一些必修课程及其先修课程的关系。
二、什么是拓扑排序?
2.1 拓扑排序的定义
通过拓扑排序得到的拓扑序列使得AOV网中所有应存在的前驱和后继关系都能得到满足。
2.2 拓扑排序的基本思想
- 从有向图中选一个无前驱的结点输出;
- 将此结点和以它为起点的弧删除;
- 重复1、2,直到不存在无前驱的结点;
- 若此时输出的结点数小于有向图中的顶点数,则说明有向图存在回路;否则,输出的顶点的顺序即为一个有向无环图的拓扑序列。
下面给出一个例子。
注意:AOV网的拓扑序列不是唯一的。
三、拓扑排序算法的实现
3.1 算法思想
3.1.1 设置辅助数组indegree[ ]
indegree[ ]数组用来存放各顶点的入度。于是有:
- 找G中无前驱的顶点,即查找indegree[i]为零的顶点 i ;
- 删除以 i 为起点的所有弧,即对链在顶点 i 后的所有邻接顶点 k,将对应的indegree[k]减1。
3.1.2 设置辅助栈
为了避免重复检测入度为零的顶点,设置一个辅助栈。若某一顶点的入度减为0,则将它入栈;每当输出某一顶点时,便将它从栈中删除。
3.1.3 算法思想
1、首先求出各顶点的入度,并将入度为0的顶点入栈;
2、只要栈不空,则重复下面的处理:
- 将栈顶顶点 i 出栈打印;
- 将顶点 i 的每一个邻接点 k 的入度减1,如果顶点 k 的入度变为0,则将顶点 k 入栈。
3.2 算法实现
3.2.1 求入度
void FindID(AdjList *G, int indegree[]) {
int i;
ArcNode *p;
/* 初始化辅助数组indegree[] */
for (i = 0; i < G->vexnum; ++i)
indegree[i] = 0;
/* 求入度 */
for (i = 0; i < G->vexnum; ++i) {
p = G->vertex[i].firstarc;
while (p != NULL) {
++indegree[p->adjvex]; /* p->adjvex即顶点i的邻接点 */
p = p->nextarc;
}
}
}
3.2.2 拓扑排序
bool TopoSort(AdjList *G) {
Stack *S;
int indegree[MAX_VERTEX_NUM];
int i, count, k;
ArcNode *p;
/* 求入度 */
FindID(G, indegree);
/* 初始化辅助栈 */
InitStack(&S);
/* 将入度数为0的顶点压入栈中 */
for (i = 0; i < G->vexnum; ++i)
if (indegree[i] == 0)
Push(S, i);
count = 0; /* count用于计算已删除的顶点个数 */
while (!IsEmpty(S)) {
Pop(S, &i);
printf("%s ", Vertex2Name(G->vertex[i].data));
++count;
/* 更新入度数 */
p = G->vertex[i].firstarc;
while (p != NULL) {
k = p->adjvex;
--indegree[k];
/* 判断当前顶点是否满足入栈条件 */
if (indegree[k] == 0)
Push(S, k);
p = p->nextarc;
}
}
ClearStack(S);
/* false表明该有向图含有回路 */
return count < G->vexnum ? false : true;
}