关键路径算法是在AOE网中找出完成所有活动耗时最长的路径的方法。
程序中涉及的结构:
typedef char VertexType; typedef int EdgeType; //邻接节点结构 typedef struct EdgeNode { int adjvex; EdgeType weight; struct EdgeNode *next; }EdgeNode; //顶点节点列表 typedef struct VertexNode { VertexType data; EdgeNode *firstedge; }VertexNode,AdjList[MAXVEX]; //图的总述结构 typedef struct { AdjList adjList; int numVertexes,numEdges; }GraphAdjList;
拓补排序基本思想就是:逐步找出AOV网中入度为0的节点。输出该节点后将与它相邻的节点的入度减一,如果此时该节点入度为零就输出。
输出入度为0的节点过程中,可以借用栈等数据结构完成输出。
PS:拓补序列不为1,同一个网可以有多个不同的拓补序列。
拓补排序代码如下:
int *etv,*ltv; int *stack2; int top2; /* 利用拓补排序判断一个网是否有环路 */ Status TopologicalSort(GraphAdjList GL) { EdgeNode *e; int i,k,gettop; int top = 0; int count = 0; int *stack; stack = (int *)malloc(GL->numVertexes * sizeof(int)); for(i=0;i<GL->numVertexes;i++) { if(0 == GL->adjLIst[i].in) { top = top + 1; stack[top] = i; } } top2 = 0; ltv = (int *)malloc(GL->numVertexes * sizeof(int)); for(i=0;i<GL->numVertexes;i++) { ltv[i] = 0; } stack2 = (int *)malloc(GL->numVertexes * sizeof(int)); while(0 != top) { gettop = stack[top]; top = top - 1; count = count + 1; top2 = top2 + 1; stack2[top2] = gettop; for(e=GL->adjList[gettop].firstedge;e;e=e->next) { k = e->adjvex; if(!(--GL->agjList[k].in)) { top = top + 1; stack[top] = k; } /* 计算事件发生最晚时间,即走最大路径 */ if((ltv[gettop]+e->weight)>ltv[k]) { ltv[k] = ltv[gettop] + e->weight; } } } if(count < GL->numVertexes) { return ERROR; } else { return OK; } }
关键路径代码如下:
/* 求关键路径 */ void CriticalPath(GraphAdjList GL) { EdgeNode *e; int i,gettop,k,j; int ete,lte; TopologicalSort(GL); etv = (int *)malloc(GL->numVertexes * sizeof(int)); /* 用终点(汇点)的最晚发生时间来初始化其他节点的最早发生时间 对终点来说,最晚发生时间=最早发生时间 */ for(i=0;i<GL->numVertexes;i++) { etv[i] = ltv[GL->numVertexes - 1]; } /* 按拓补序列逆序计算事件的最早发生时间 */ while(0 != top2) { gettop = stack2[top2--]; for(e=GL->adjList[gettop].firstedge;e;e=e->next) { /* k此时是与gettop邻接的边的终点 */ k = e->adjvex; /* e->weight是从gettop到k这个边的权值 最早发生时间=k节点的最早发生时间-耗时最长的权重 */ if(etv[k] - e->weight < etv[gettop]) { etv[gettop] = etv[k] - e->weight; } } } /* ete:活动的最早开工时间 lte:活动的最晚开工时间 etv:事件的最早发生时间 ltv:事件的最晚发生时间 */ for(j=0;j<GL->numVertexes;j++) { for(e=GL->adjList[j].firstedge;e;e=e->next) { /* k是边的终点 j是边的起点 e是指j到k这条边 */ k = e->adjvex; /* */ lte = ltv[j]; /* 活动的最早发生时间=活动终点事件最早发生时间-活动耗时(权重) */ ete = etv[k] - e->weight; /* 比较的是活动的最早和最晚时间 */ if(ete == lte) { printf("<v%d,v%d> length:%d ",GL->adjList[j].data,GL->adjList[k].data,e->weight); } } } }
ete:活动的最早开工时间
lte:活动的最晚开工时间
etv:事件的最早发生时间
ltv:事件的最晚发生时间
事件的最晚发生时间是从AOE网的起点开始,到达时间所需耗时(权重)最大的值。如下图
事件的最早发生时间是和事件的最晚发生时间逆向计算的。事件的最晚发生时间,是从起点开始,往终点推算;事件的最早发生时间是从AOE网的终点开始,往起点逆向推算。
事件的最早发生时间=后继事件的最晚发生时间-MAX(权重最大的边),如下图:
PS:对于AOE网的终点来说,事件的最早发生时间 = 事件的最晚发生时间
活动的最晚发生时间=活动的起点事件的最晚发生时间
活动的最早发生时间=活动的终点事件的最早发生时间 - 活动的耗时(边的权重)
所谓的关键活动就是:活动的最早发生时间 = 活动的最晚发生时间。根本没有选择的余地的。
理清了这四个时间,关键路径算法就没什么了。