数据结构总结
拓扑排序
1 拓扑排序(在没有回路的情况下实现)
概念
由某个元素集合上的一个偏序得到该集合上的一个全序,该操作称为拓扑排序。
应用: 检查有向图是否存在回路。
没有回路,有先后关系,可表示工程施工计划,除协助制定施工序列外,还可判断计划是否有问题(工程能否顺利开展)
AOV-网:顶点表示活动,弧表示活动间先后(依赖)关系的有向图。
2 实现拓扑排序
算法原理
g比较特殊,因为它有前驱
代码实现
CountInDegree(G, indegree)
{
for(int i=0; i<G.vexnum; ++i)
for(p=G.vertices[i].firstarc; p!=NULL; p=p->nextarc)
++indegree[p->adjvex];
}//找出每个点的入度数
外层循环遍历图G的所有顶点,从0到G.vexnum-1。
内层循环遍历当前顶点i的所有邻接边,从G.vertices[i].firstarc开始,直到p为NULL。
对于每条邻接边p,将p->adjvex对应的入度数组indegree的值加1。
Status TopologicalSort(ALGraph G)
{
CountInDegree(G, indegree);//找出每个点的入度数
InitStack(S);
for(i=0; i<G.vexnum; ++i)
if(!indegree[i]) Push(S,i);//入度为0的顶点入栈
count = 0; //用以对输出的顶点进行计数
while (!StackEmpty(S))
{
Pop(S,i);
OutPutElem (G.vertices[i].data);
++count;
for(p=G.vertices[i].firstarc; p!=NULL; p=p->nextarc)
{
k=p->adjvex;
- -indegree[k];
if(!indegree[k]) Push(S,w);//新产生的入度为0的顶点入栈
}
}
if(count<G.vexnum) return ERROR;//图中有回路
else return OK;//图中没有回路
}//T(n)=O(n+e)
关键路径
概念
最长路径
时间
补充说明
求事件最早发生时间和关键路径的快速方法
算法实现
所有事件状态的最早激活时间
与拓扑排序基本相同,多了一个各点ve初始化零及更新后继节点最早激活时间的步骤;为方便后面求各顶点最晚激活时间还要用栈T记录拓扑逆序
Status GetVEAndRvsTopOrd(ALGraph G,Stack &T){
//求各顶点最早到达时间计入全局数组ve,T按拓扑序存储顶点
FindInDegree(G,indegree);
InitStack(S); for(i=0;i<G.vexnum;++i) if(!indegree[i]) Push(S,i);
ve[0..G.vexnum-1]=0;
InitStack(T); //用来求拓扑逆序
count=0; //对输出的顶点进行计数
while (!StackEmpty(S)){//出栈S、入栈T、”删除”、更新
Pop(S,j); ++count;
Push(T,j);
for(p=G.vertices[j].firstarc; p; p=p->nextarc){
k=p->adjvex; --indegree(k);
if(indegree[k]= =0) Push(S, k);
if( ve[j]+*(p->info) > ve[k] ) ve[k] = ve[j]+*(p->info);//info存权
}//for
}//for
if(count<G.vexnum)return ERROR; else return OK;}
关键路径
关键路径: 调用子函数求ve,按拓扑逆序求vl,求ee/el并输出关键否
Status CriticalPath(ALGraph G){
InitStack(T); // 初始化栈T
if(! GetVEAndRvsTopOrd(G,T) ) return ERROR; // 获取顶点的最早激活时间和拓扑排序
vl [0..G.vexnum-1]=ve[G.vexnum-1]; // 将最晚激活时间初始化为最后一个顶点的最早激活时间
while(!StackEmpty(T)){
Pop(T,j); // 弹出栈顶元素j
for(p=G.vertices[j].firstarc; p!=NULL; p=p->nextarc){
k=p->adjvex; dut=*(p->info); // 获取邻接顶点k和持续时间dut
if( vl[k]-dut < vl[j] ) vl[j]= vl[k]-dut; // 更新最晚激活时间
}
}
for(j=0;j<G.vexnum;++j)
for(p=G.vertices[j].firstarc;p;p=p->nextarc){
k=p->adjvex; dut=*(p->info);
ee=ve[j]; el=vl[k]-dut//活动<j,k>的最早/迟开始时间
if(ee==el) Print (j,k,dut,ee,el, √ ); // 如果最早和最晚时间相等,则输出为关键活动
else Print (j,k,dut,ee,el, × ); // 否则输出为非关键活动
}}
所有事件状态的最晚到达时间
所有活动的最早最晚激活时间(e和l通过ve和vl求得)
最短路径
求解最短路的Dijkstra算法
Dijkstra算法思想
没有同时经过v1,v2
Dijstra算法实现
代码原理
没有直接相连为无穷值
代码
原理理解
初始化从u
判断dist, c的最小,从c
判断dist,除了c,a最小,从a,更新dist
判断dist,除了c,a,d小,从d,更新dist
finished[k]标记k号顶点是否处理结束
D[k]存储源到顶点k的当前最短路长度
P[v] 记录源到v的当前最短路径
finished[v0]=TRUE;
D[v0]=0;
void ShortestPath_FLOYD(MGraph G, int v0,PathArray &P, DistancArray &D){
(1)源标记Finished,初始化源到V-S中各点直接距离
for(v=0; v<G.vexnum; ++v)
{
if(v!=v0)
{
finished[v]=FALSE;
D[v]=G.arcs[v0][v];
}
if(D[v]<INFINITY)
{
P[v] = v0 -> v;//P[v]被赋值为从v0到v的路径。
}
}
(2)未Finished的点中找距离最小点,标记其Finished
for(i=1; i<=G.vexnum-1; ++i)
{
min=INFINITY;
for(w=0; w<G.vexnum; ++w)
if(!finished[w]&&D[w]<min)
{
v=w;
min=D[w];
}
finished[v]=TRUE;
(3)根据新finished顶点更新源到其他点的距离
for(w=0; w<G.vexnum; ++w)
{
if( !finished[w] && (min+G.arcs[v][w])< D[w] )
{
D[w]= min+G.arcs[v][w];
P[w] =strcat(P[v], “->w”);
}
}
}
(3)根据新finished顶点更新源到其他点的距离
}
复杂度 T(n)=O(G.vexnum2 ) 顶点个数的平方
Floyd算法
Floyd算法原理
Step0:计算从i出发、跳点为空、直接到j的最短路. 此时最短路对应的距离记入矩阵D(0)[i][j]
Step1:可选跳点为{1}时,i到j的路径分两种情况, 或者不经过1,此时最短距离为D(0)[i][j], 或者经过1, 此时最短为D(0)[i][1]+D(0)[1][j] D(1)[i][j]=min{D(0)[i][j], D(0)[i][1]+D(0)[1][j]}
加入a,更新路径表
Step2:可选跳点为{1,2}时i到j的路径分两种情况, 或者不经过2,此时最短距离为D(1)[i][j], 或者经过2, 此时最短为D(1)[i][2]+D(1)[2][j] D(2)[i][j]=min{D(1)[i][j], D(1)[i][2]+D(1)[2][j]}
加入b,更新路径表
Step k:可选跳点为{1,2,…,k}(下标不超过k)时, 或者不经过k,此时最短距离为D(k-1)[i][j], 或者经过k, 此时最短为D(k-1)[i][k]+D(k-1)[k][j] D(k)[i][j]=min{D(k-1)[i][j], D(k-1)[i][k]+D(k-1)[k][j]}
Step n:可选跳点为{1,2,…,n}时,意味着对中间经过的顶点不加任何限制,此时所得最短路就是全局最短路 D(n)[i][j]= min{D(n-1)[i][j],D(n-1)[i][n]+D(n-1)[n][j] }
总结
D(0)[i][j]=G.arcs[i][j]
D(k)[i][j]=min{D(k-1)[i][j], D(k-1)[i][k]+D(k-1)[k][j] }
Floyd算法实现
void GetShortestPath_FLOYD (MGraph G,
PathMatrix &P, DistancMatrix &D){
//初始化D(0)[][]与P(0)[][]
for(i=1; i<=G.vexnum; ++i){
for(j=1; j<=G.vexnum; ++j){
D[i][j] = G.arcs[i][j];
if(D[i][j] != INFINITY){
P[i][j] = i + “->” + j;
}
}
}
//迭代求Dk[][]与Pk[][],k=1 ->G.vexnum
for(k=1; k<=G.vexnum; ++k){
for(i=1; i<=G.vexnum; ++i)
for(j=1; j<=G.vexnum; ++j)
if(D[i][k]+D[k][j] < D[i][j]){
D[i][j] = D[i][k]+ D[k][j];
P[i][j] = MyStrCat(P[i][k],P[k][j]);
}
}
总结与推广
1.
2.动态规划法