背景
用【有向图】表示一个【工程的施工图】或【程序的数据流图】,则图中不允许出现回路
例子:
【回路】 打地基->做房子结构->砌墙->装修->打地基
【解释】 “做房子结构“的前提是“打地基”,“砌墙”的前提是“做房子结构”,“装修”的前提是“砌墙”,“打地基”的前提是“装修”
【说明】 到底是哪一个先开始,有回路就没法做了
拓扑排序
如何检查【有向图】中是否存在【回路】的方法之一:
对【有向图】进行【拓扑排序】
拓扑排序是什么
按照【有向图】给出的【次序关系】,将图中【顶点】排成一个【线性序列】,对于【有向图】中没有限定次序关系的顶点,则可以人为加上任意的次序关系
这样得到的【线性序列】称为【拓扑有序序列】
【拓扑排序】的目标呢:就是构造一个【拓扑有序序列】
如何进行拓扑排序
- 从【有向图】中选取一个没有前驱的顶点,并输出=>选【入度为0的顶点】
- 从有向图中删去【此顶点】以及所有【以它为尾的弧】=>弧头顶点的入度减1
- 重复以上两步,直至【图空】,或者【图不空但找不到无前驱的顶点】为止
举例
例一
步骤数 | 入度为0的有 | 选择顶点 并删除顶点与弧 |
输出 |
---|---|---|---|
1 | A,B,C | A | “A” |
2 | B,C | B | “AB” |
3 | D,C | D | “ABD” |
4 | F,G,C | F | “ABDF” |
5 | G,C | G | “ABDFG” |
6 | C | C | “ABDFGC” |
7 | E | E | “ABDFGCE” |
8 | H | H | “ABDFGCEH” |
9 | I | I | “ABDFGCEHI” |
图空,没有回路,”ABDFGCEHI”为【拓扑有序序列】
例二
步骤数 | 入度为0的有 | 选择顶点 并删除顶点与弧 |
输出 |
---|---|---|---|
1 | A,C | A | “A” |
2 | C | C | “AC” |
找不到入度为0的顶点=>退出=>图还没有空图=>有回路
定量分析
思路
取入度为0的顶点v;
while (v<>0) {
printf(v); ++m;
// 所有入度减1
w:=FirstAdj(v);
while (w<>0) {
inDegree[w]--;
w:=nextAdj(v,w);
}
取下一个入度为0的顶点v
}
if m<n printf("图中有回路")
优化
为避免每次都要搜索【入度为0的顶点】
在算法中设置一个“栈”,以保存“入度为0”的顶点
CountInDegree(G, indegree); //对各个顶点求入度
InitStack(S);
for (i=0; i<G.vexnum; ++i) {
if (!indegree[i]) Push(S,i); //入度为0的顶点入栈
}
count = 0; //对输出顶点计数
while (!EmptyStack(S)) {
Pop(S,v); ++count; printf(v);
for (w=FirstAdj(v); w; w=NextAdj(G,v,w)) {
--indegree(w);
}
//新产生的入度为零的顶点入栈
if (!indegree[w]) Push(S,w);
}
if (count<G.vexnum) printf("图中有回路")