7.6 两点之间的最短路径问题
从原点到其余各点的最短路径。
算法的基本思想是:依路径长度递增的次序求得各条路径
假设图中所示为从原点到其余各点之间的最短路径,则在这些路径中,必然存在一条长度最短者。
在这条路径上,必定只含有一条(权值最小)弧,由此,只要在所有从原点出发的弧中查找权值最小者;
长度次短的路径可能有两种情况:它可能是从原点直接到改点的路径;也可能是,从原点a再到该点
其余依次类推。
假设Disk[k]表示当前所求得的从原点到顶点k的最短路径。则一般情况下,Disk[k]=<源点到顶点k的弧上的权值>或者=<源点到其它顶点路径的长度>+<其它顶点到顶点k弧上的权值>
每一对顶点之间的最短路径
算法的基本思想:从vi到vj的所有存在的路径中,选出一条最短的路径
若<vi,vj>存在,则存在路径{vi,vj} 路径中不含其它顶点
若<vi,vl><vl,vj>存在,则存在路径{vi,vl,vj} 路径中所含顶点序号不大于1
若{vi,…,v2},{v2,…,vj}存在,则存在一条路径{vi,…,v2,…vj} 路径中所含顶点序号不大于2
依次类推,则vi到vj的最短路径应是上述着这些路径中,路径长度最小者
7.7拓扑排序
问题:假设以有向图表示一个工程的施工图或程序的数据流图,则图中不允许出现回路。
如何检查有向图是否存在回路的方法之一,是对有向图进行拓扑排序。
何谓“拓扑排序”?
对有向图进行如下操作:
按照有向图给出的次序关系,就爱那个图中顶点排成一个线性序列,对于有向图中没有限定次序关系的顶点,则可以人为加上任意的次序关系。
由此所得顶点的线性序列称之为拓扑有序序列
例如:对于下列有向图
可求得拓扑有序序列:ABCD或ACBD
反之,对于下列有向图
不能求得它的拓扑有序序列。因为图中存在一个回路{B,C,D}
如何进行拓扑排序?
一、从有向图中选取一个没有前驱的顶点,并输出之
二、从有向图中删去此顶点以及所有以它为尾的弧
重复上述两步,直至图空,或者图不空但找不到前驱的顶点为止。
没有前驱的顶点===入度为零的顶点
删除顶点及以它为尾的弧===弧头顶点的入度减1
算法取入度为零的顶点v
29_001 |
while(v <> 0) { printf(v); ++ m; w: = FirstAdj(v); while(w <> 0) { inDegree[w] --; w: = newAdj(v, w); } //取下一个入度为零的顶点v; } if(m < n) { printf("图中有回路"); } |
为避免每次都要搜索入度为零的顶点,在算法中设置一个“栈”,以保存“入度为零”的顶点。
29_002 |
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的顺序应该是按拓扑逆序的次序。因为,脱兔逆序序列即为拓扑有序序列的逆序列,因此,应该在拓扑排序的过程中,另设一个“栈”记下拓扑有序序列。
1. 熟悉图的各种存储结构及其构造算法,了解实际问题的求解效率与采用何种存储结构和算法有密切联系。
2. 熟练掌握图的两种搜索路径的遍历;遍历的逻辑定义,深度优先搜索的来年各种形式(递归和非i递归)和广度优先搜索的算法,在学习中应注意图的遍历算法域树的遍历算法之间的类似和差异。
3. 应用图的遍历算法求解各种简单路径问题
4. 理解教科书中讨论的各种图的算法。
5.4 广义表的类型定义
ADT Glist{
数据对象:D={ei|I= 1,2,…,n; n>=0; e∈AtomSet或ei∈GList,AtomSet为某个数据对象}
数据关系:LR={<ei-1,ei>|ei-1,ei∈D,2<=i<=n}
}
广义表是递归定义的线性结构。
一般情况下,广义表写成LS=(a1,a2,…,an)其中:ai或为原子或为广义表
广义表是一个多层次的线性结构
例如:D=(E,F) E=(a,(b,c)) F=(d,(e))
其它如:
A=()
B=(a,B)=(a,(a,(a,…,)))
C=(A,D,F)
广义表LS=(a1,a2,…an)的结构特点:
1) 广义表中的数据元素有相对次序
2) 广义表的长度定义为最外层包含元素个数
3) 广义表的深度定义为所含括弧的重数;
注意“原子”的深度为“0”。“空表”的深度为1
4) 广义表可以共享
5) 广义表可以是一个递归的表;递归表的深度是无穷值,长度是有限值。
6) 任何一个非空广义表LS(a1,a2,…,an)均可分解为表头Head(L,S)=a1和表尾Tail(LS)=(a2,…,an)两部分
例如:LS=(A,D)=((),(E,F))=((),((a,(b,c)),F)
Head(LS)=A Tail(LS)=(D)
Head(D)=E Tail(D)=(F)
Head(E)=a Tail(E)=((b,c))
Head(((b,c)))=(b,c) Tail(((b,c))=()
Head((b,c))=b Tail(b,c))=(c)
Head((c))=c Tail((c))=()
基本操作:
结构的创建和销毁
InitGList(&L);
DestroGList(&L);
CreateGList(&L, S);
CopyGList(&T, L)
状态函数
GListLength(L);
GListDepth(L);
GListEmpty(L);
GetHead(L);
GetTail(L);
插入和删除操作
InsertFirst(&L, e);
DeleteFirst(&L, &e);
遍历
Traverse_GL(L, Visit());