关键路径(C语言简单实现)

关键路径(C语言简单实现)

本文参考自《大话数据结构》,对关键路径的理解参考自:https://www.jianshu.com/p/1857ed4d8128

在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,我们称之为AOE 。我们把AOE网中没有入边的顶点称为始点或源点,没有出边的顶点称为终点或汇点。

尽管AOE网和AOV网都是用来对工程建模的,但它们还是有很大的不同,主要体现在AOV网是顶点表示活动的网,只描述活动之间的制约关系,而AOE网是用边表示活动的网,边上的权值表示活动持续的时间。

关键路径算法原理

​ 为了讲清楚求关键路径的算法,我还是来举个例子。假设一个学生放学回家,除掉吃饭、洗漱外,到睡觉前有四小时空闲,而家庭作业需要两小时完成。不同的学生会有不同的做法,抓紧的学生,会在头两小时就完成作业,然后看看电视、读读课外书什么的;但也有超过一半的学生会在最后两小时才去做作业,要不是因为没时间,可能还要再拖延下去。
​ 这里做家庭作业这活动的最早开始时间是四小时的开始,可以理解为0,而最晚开始时间是两小时之后马上开始,不可以再晚,否则就是延迟了,此时可以理解为2。显然,当最早和最晚开始时间不相等时就意味着空闲。
​ 接着,你老妈发现了你拖延的小秘密,于是买了很多的课外习题,要求你四个小时,不许有一丝空闲,省得你拖延或偷懒。此时整个四小时全部被占满,最早开始时间和最晚开始时间都是0,因此它就是关键活动了。
也就是说,我们只需要找到所有活动的最早开始时间和最晚开始时间,并且比较它们,如果相等就意味着此活动是关键活动,活动间的路径为关键路径。如果不等,则就不是。

我们把路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动

几个关键参数

  1. 事件的最早发生时间etv:即顶点vk的最早发生时间;
  2. 事件的最晚发生时间ltv:即顶点vk的最晚发生时间,也就是每个顶点对应的事件最晚需要开始的时间,超出此时间将会延误整个工期;
  3. 活动的最早开工时间ete:即弧ak的最早发生时间;
  4. 活动的最晚开工时间lte:即弧ak的最晚发生时间,也就是不推迟工期的最晚开工时间。

可以由1和2求的3和4,然后再根据ete[k]是否与lte[k]相等来判断ak是否是关键活动

关键路径算法

与拓扑排序时邻接表结构不同的地方在于,弧链表增加了weight域,用来存储弧的权值。

求事件的最早发生时间etv的过程,就是我们从头到尾找拓扑的过程,因此,在求关键路径之前,需要先调用一次拓扑序列算法的代码来计算etv和拓扑序列列表。为此,我们先在程序开始处声明几个全局变量。

int *etv, *ltv;	//事件最早发生时间和最迟发生时间数组
int *stack2;	//用于存储拓扑序列的栈
int top2;	//用于stack2的指针

下面是改进过的求拓扑序列算法:

Status TopologicalSort(GraphAdjList GL){
   
	EdgeNode *e;
	int i, k, gettop;
	int top = 0;	//用于栈指针下标
	int count = 0;	//用于同级输出顶点的个数
	int *stack;	//建栈将入度为0的顶点入栈
	stack = (int*)malloc(GL->numVertexes * sizeof(int));
	for(i=0;i<GL->numVertexes;i++)
		if(0 == GL->adjList[i].in)
			stack[++top] = i;
	top2 = 0;	//初始化为0
	etv = (int*)malloc(GL->numVertexes*sizeof(int));	//事件最早发生时间
	for(i=0;i<GL->numVertexes;i++)
		etv[i] = 0;	//初始化为0
	stack2 = (int*)malloc(GL->numVertexes*sizeof(int));	//初始化
	while(top!=0){
   
		gettop=stack[top--];
		count++;	
		stack2[++top2] = gettop;	//将弹出的顶点序号压入拓扑序列的栈
		for(e=GL->adjList[gettop].firstedge;e;e=e->next){
   
			k = e->adjvex;
			if(!(--GL->adjList
  • 9
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
关键路径是指在一个有向无环图中,从起点到终点的最长路径。关键路径上的每个节点都是整个图中的关键节点,这些节点的最早开始时间和最迟开始时间相等。 下面是使用 C 语言实现有向网的关键路径的一般步骤: 1. 定义有向网的数据结构,包括节点、边和权值等信息。 2. 构建有向网,读入节点、边和权值等信息,保存到数据结构中。 3. 计算每个节点的最早开始时间(EST)和最迟开始时间(LST)。 4. 计算每条边的最早开始时间(EET)和最迟开始时间(LET)。 5. 根据节点和边的计算结果,确定关键路径上的节点和边,输出关键路径和关键节点。 下面是一个简单的 C 语言示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_VERTEX_NUM 100 // 最大节点数量 typedef struct { int adj; // 相邻节点编号 int weight; // 权值 } ArcNode; typedef struct { char name[20]; // 节点名称 int inDegree; // 入度 int outDegree; // 出度 int earliest; // 最早开始时间 int latest; // 最迟开始时间 ArcNode *adjList; // 相邻节点列表 } VertexNode; typedef struct { VertexNode vertex[MAX_VERTEX_NUM]; // 节点列表 int vertexNum; // 节点数量 } Graph; void initGraph(Graph *G) { memset(G, 0, sizeof(Graph)); } int findVertex(Graph *G, char *name) { for (int i = 0; i < G->vertexNum; i++) { if (strcmp(G->vertex[i].name, name) == 0) { return i; } } return -1; } void addVertex(Graph *G, char *name) { if (findVertex(G, name) != -1) { return; } strcpy(G->vertex[G->vertexNum].name, name); G->vertexNum++; } void addEdge(Graph *G, char *start, char *end, int weight) { int i = findVertex(G, start); int j = findVertex(G, end); if (i == -1 || j == -1) { return; } ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode)); p->adj = j; p->weight = weight; p->next = G->vertex[i].adjList; G->vertex[i].adjList = p; G->vertex[j].inDegree++; G->vertex[i].outDegree++; } int topologicalSort(Graph *G, int *topoSeq) { int n = 0; int *inDegree = (int*)malloc(sizeof(int) * G->vertexNum); memset(inDegree, 0, sizeof(int) * G->vertexNum); for (int i = 0; i < G->vertexNum; i++) { inDegree[i] = G->vertex[i].inDegree; } int *stack = (int*)malloc(sizeof(int) * G->vertexNum); int top = -1; for (int i = 0; i < G->vertexNum; i++) { if (inDegree[i] == 0) { stack[++top] = i; } } while (top != -1) { int i = stack[top--]; topoSeq[n++] = i; for (ArcNode *p = G->vertex[i].adjList; p; p = p->next) { int j = p->adj; if (--inDegree[j] == 0) { stack[++top] = j; } } } free(inDegree); free(stack); if (n == G->vertexNum) { return 1; } else { return 0; } } int criticalPath(Graph *G) { int *topoSeq = (int*)malloc(sizeof(int) * G->vertexNum); if (!topologicalSort(G, topoSeq)) { return 0; } for (int i = 0; i < G->vertexNum; i++) { G->vertex[topoSeq[i]].earliest = 0; } for (int i = 0; i < G->vertexNum; i++) { int j = topoSeq[i]; for (ArcNode *p = G->vertex[j].adjList; p; p = p->next) { int k = p->adj; int t = G->vertex[j].earliest + p->weight; if (t > G->vertex[k].earliest) { G->vertex[k].earliest = t; } } } int end = topoSeq[G->vertexNum - 1]; G->vertex[end].latest = G->vertex[end].earliest; for (int i = G->vertexNum - 2; i >= 0; i--) { int j = topoSeq[i]; G->vertex[j].latest = G->vertex[end].latest; for (ArcNode *p = G->vertex[j].adjList; p; p = p->next) { int k = p->adj; int t = G->vertex[k].latest - p->weight; if (t < G->vertex[j].latest) { G->vertex[j].latest = t; } } } int critical = 0; for (int i = 0; i < G->vertexNum; i++) { for (ArcNode *p = G->vertex[i].adjList; p; p = p->next) { int j = p->adj; int eet = G->vertex[i].earliest; int let = G->vertex[j].latest - p->weight; if (eet == let) { printf("%s -> %s is a critical edge.\n", G->vertex[i].name, G->vertex[j].name); critical = 1; } } } free(topoSeq); return critical; } int main() { Graph G; initGraph(&G); addVertex(&G, "A"); addVertex(&G, "B"); addVertex(&G, "C"); addVertex(&G, "D"); addVertex(&G, "E"); addEdge(&G, "A", "B", 3); addEdge(&G, "A", "C", 2); addEdge(&G, "B", "D", 4); addEdge(&G, "C", "D", 5); addEdge(&G, "B", "E", 6); addEdge(&G, "D", "E", 1); if (!criticalPath(&G)) { printf("There is no critical path.\n"); } return 0; } ``` 上述代码实现了有向网的关键路径,包括了关键路径上的节点和边的计算,以及输出关键路径和关键节点。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值