严蔚敏视频 笔记29
每一对顶点之间的最短路径
从vi到vj的所有存在路径中选出一条长度最短的路径
算法:
若<vi,vj>存在,则存在路径{vi,v1,vj}
// 路径中所含顶点序号不大于1
若{vi,…,v2},{v2,…,vj}存在
则存在一条路径{vi,…,v2,…,vj}
// 路径中所含顶点序号不大于2
…
依此类推,则vi至vj的最短路径应是上述这些路径中长度最小者
7.7 拓扑排序
以有向图表示一个工程的施工图或程序的数据流图,则图中不允许出现回路
拓扑排序
按照有向图给出的次序关系,将图中顶点排成一个线性序列,对于有向图中没有限定次序关系的顶点,则可以人为加上任意的次序关系
由此得拓扑有序序列
如何进行拓扑排序
一、从有向图中选取一个没有前驱的顶点,并输出
二、从有向图中删除此顶点以及所有从它为尾的弧
三、重复上述两步,直至图空,或者图不空但找不到无前驱的顶点为止
无前驱的顶点==入度为0的顶点
删除顶点及以它为尾的弧==弧头顶点的入度减1
取入度为零的顶点v:
while(v!=0) {
printf(v); ++m;
w=FirstAdj(v);
while(w!=0) {
InDegree[w]--;
w=NextAdj(v,w);
}
// 取下一个入度为0的顶点
if(m<n) printf("图中存在回路");
}
为避免每次都要搜索入度为0的顶点,设置栈
CountInDegree(G,indegree); // 对各顶点求入度
InitStack(S);
for(i=0;i<G.vexnum;++i)
if(!indegree[i]) Push(S,i); // 入度为零的顶点入栈
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("图中有回路");
7.8 关键路径
以有向网表示一施工流图,弧上权值表示完成该项子工程所需时间,求将影响整个工程完成期限的子工程项
整个工程完成的时间为从有向图的源点到汇点的最长路径
关键活动:该弧上的权值增加将使有向图上的最长路径的长度增加
“事件(顶点)”的最早发生时间ve(j)
ve(j)=从源点到顶点j的最长路径长度
“事件(顶点)”的最迟发生时间vl(k)
vl(k)=从顶点k到汇点的最短路径长度
假设第i条弧为<j,k>
则第i项活动
“活动(弧)”的最早开始时间ee(i)
ee(i)=ve(j)
“活动(弧)”的最晚开始时间el(i)
el(i)=vl(k)-dut(<j,k>)
事件发生时间的计算公式
ve(源点)=0
ve(k)=Max{ve(j)+dut(<j,k>)}
vl(汇点)=ve(汇点)
vl(j)=Min{vl(k)-dut(<j,k>)}
算法实现要点:
求ve的顺序应该是按拓扑有序的次序
求vl的顺序应该是按拓扑逆序的次序
因为拓扑逆序序列即为拓扑有序序列的逆序列
应该在拓扑排序的过程中另设一个“栈”记下拓扑有序序列