在图结构中,源点到终点的所有路径中,其中具有最大路径长度的路径称为关键路径。
(1) 事件Vk的最早发生时间:指顶点活动从源点V1到终点Vk的最长路径长度;
如下图中,顶点D活动的最早发生时间为2+4=6,之所以称之为最早,是因为即便A->B->D很快,但是也必须等到C完成后D才能开始活动
事件Vk的最早发生时间计算过程:
- 通过拓扑排序的顺序依次遍历顶点得到Vk,通过顶点Vk获取所有后继顶点
- 比较【顶点Vk的最早发生时间 + 边大小】和【后继顶点的最早发生时间】两者大小
- 选择最大值作为后继顶点的最早发生时间
例如:顶点C,后继顶点为D、F,【C最早发生时间 (2) + 边大小(4)】>【D的最早发生时间(5)】 ,则D的最早发生时间 = 2 + 4 = 6
(2) 事件Vk的最迟发生时间:指Vk在整个过程(源点到终点的最大时长)的前提下,保证后继事件能够发生的最迟时间
如下图中,源点A到终点F的最长时长 = 8,在此基础下,顶点活动E的最迟发生时间 = 7
如果E的最迟发生时间一旦超过7,则路径中通过【顶点活动E最迟发生时间(>7) + 边大小(1)】就会比原来的【关键路径长度(8)】花的时间更久,也就相当于活动E会拖慢整个工程的进度。
事件Vk的最早发生时间计算过程:
- 通过逆拓扑排序的顺序依次遍历顶点得到Vk,通过顶点Vk获取所有后继顶点
- 比较【后继顶点的最迟发生时间 - 边大小】和【顶点Vk的最迟发生时间】两者大小
- 选择最小值作为顶点Vk的最迟发生时间
例如:顶点C,后继顶点为D、F,【F最迟发生时间 (8) - 边大小(3)】= 5,【D最迟发生时间 (6) - 边大小(4)】= 2,则C的最迟发生时间 = min(5,2) = 2;
(3)【最迟发生时间】 - 【最早发生时间】 = 0 的活动构成关键路径
#include<stdio.h>
#include<stdlib.h>
#define MaxVex 20
typedef int EdgeType;
typedef char VertexType;
// 边结点结构(链表)
typedef struct EdgeNode
{
int adjvex; // 该边邻接的结点下标
EdgeType edgeData; // 该边结点的数据(权值)
struct EdgeNode *next;
}EdgeNode, *EdgeList;
// 顶点结点结构(顺序表)
typedef struct
{
VertexType vextexData; // 顶点数据
EdgeList edgeList; // 顶点的边链表
int indegree; // 顶点的入度
EdgeType earliest; // 该顶点活动的最早发生时间
EdgeType latest; // 该顶点活动的最迟发生时间
}VertexNode, *VertexList;
// 邻接表结构
typedef struct
{
VertexList vertexList; // 顶点顺序表
int numVertexs, numEdges; // 顶点数
}ALGraph;
// 初始化邻接表
void InitALGraph(ALGraph *G)
{
G->numVertexs = 0;
G->numEdges = 0;
G->vertexList = (VertexNode *)malloc(MaxVex * sizeof(VertexNode));
// 初始化边链表头结点,和顶点入度
int i;
for (i = 0; i < MaxVex; i++)
{
G->vertexList[i].edgeList = (EdgeNode *)malloc(sizeof(EdgeNode));
G->vertexList[i].edgeList->next = NULL;
G->vertexList[i].indegree = 0;
G->vertexList[i].earliest = 0;
G->vertexList[i].latest = 0;
}
printf("已初始化邻接表\n\n");
}
// 创建邻接表
void CreateALGraph(ALGraph *G)
{
printf("请输入顶点数和边数:");
scanf("%d %d", &G->numVertexs, &G->numEdges);
int i, j, k;
EdgeType w;
// 输入顶点数据
for (i = 0; i < G->numVertexs; i++)
{
fflush(stdin);
printf("请输入第%d个顶点数据:", i + 1);
scanf("%c", &G->vertexList[i].vextexData);
}
// 输入边的权值
for (k = 0; k < G->numEdges; k++)
{
printf("请输入边(vi, vj)上的顶点序号及权值:");
scanf("%d %d %d", &i, &j, &w);
EdgeNode *p1;
p1 = (EdgeNode *)malloc(sizeof(EdgeNode));
// 链表头插法 插入 顶点(vi -> vj)的边
p1->edgeData = w;
p1->next = G->vertexList[i - 1].edgeList->next;
G->vertexList[i - 1].edgeList->next = p1;
p1->adjvex = j - 1; // 该边邻接着顶点j
G->vertexList[j - 1].indegree++; // 邻接顶点j的入度加一
}
printf("已创建邻接表!\n\n");
}
// 拓扑排序
void KeyPath(ALGraph G)
{
// 用一个数组来代表一个栈结构,top1为栈顶指针
int top1 = 0, Stack[MaxVex];
// 用于存储逆序拓扑排序的逆序栈
int top2 = 0, RStack[MaxVex];
// 初始化
int i, indegree[MaxVex];
for (i = 0; i < G.numVertexs; i++)
{
// 获取每个顶点的入度
indegree[i] = G.vertexList[i].indegree;
// 初始化栈,将入度为0的顶点入栈
if (indegree[i] == 0)
{
Stack[top1++] = i;
}
}
// 通过拓扑排序计算顶点活动的最早发生时间
int v;
EdgeNode *p;
printf("拓扑排序:");
while (top1 > 0)
{
// 将入度为0的顶点出栈
i = Stack[--top1];
// 入栈到逆序栈中
RStack[top2++] = i;
printf("%c--->", G.vertexList[i].vextexData);
// 遍历该顶点所有的后继顶点
for (p = G.vertexList[i].edgeList->next; p ; p = p->next)
{
v = p->adjvex;
// 将后继顶点的入度减一
indegree[v]--;
// 如果后继顶点入度为零,则入栈
if (!indegree[v])
{
Stack[top1++] = v;
}
// 更新所有后继顶点活动的最早发生时间
if (G.vertexList[i].earliest + p->edgeData > G.vertexList[v].earliest)
{
G.vertexList[v].earliest = G.vertexList[i].earliest + p->edgeData;
}
}
}
printf("\n");
if (top2 < G.numVertexs)
{
printf("排序失败!存在回路\n");
}
else
{
// 初始化顶点活动的最迟发生时间
v = RStack[--top2];
for (i = 0; i < G.numVertexs; i++)
{
G.vertexList[i].latest = G.vertexList[v].earliest;
}
// 根据逆序栈更新顶点活动的最迟发生时间
while (top2 > 0)
{
// 逆序依次获取顶点
i = RStack[--top2];
for (p = G.vertexList[i].edgeList->next; p ; p = p->next)
{
// 邻接的后继顶点
v = p->adjvex;
// 更新顶点活动的最迟发生时间
if (G.vertexList[v].latest - p->edgeData < G.vertexList[i].latest)
{
G.vertexList[i].latest = G.vertexList[v].latest - p->edgeData;
}
}
}
}
}
void Display(ALGraph G)
{
int i;
printf("顶点\t");
for (i = 0; i < G.numVertexs; i++)
{
printf("%c\t", G.vertexList[i].vextexData);
}
printf("\n最早\t");
for (i = 0; i < G.numVertexs; i++)
{
printf("%d\t", G.vertexList[i].earliest);
}
printf("\n最迟\t");
for (i = 0; i < G.numVertexs; i++)
{
printf("%d\t", G.vertexList[i].latest);
}
printf("\n关键路径:");
for (i = 0; i < G.numVertexs; i++)
{
if (G.vertexList[i].latest - G.vertexList[i].earliest == 0)
{
printf("%C--->", G.vertexList[i].vextexData);
}
}
printf("\n");
}
int main()
{
ALGraph G;
InitALGraph(&G);
CreateALGraph(&G);
KeyPath(G);
Display(G);
system("pause");
return 0;
}
#测试用例
6 8
A
B
C
D
E
F
1 2 3
1 3 2
2 4 2
2 5 3
3 4 4
3 6 3
4 6 2
5 6 1
