[Data structure] the key path of directed acyclic graph (AOE-network) (C language)

1. Basic concepts

The directed acyclic graph, which uses vertices to represent events, arcs to represent activities, and the weight of arcs to represent the time required for activities, is called a network with edges representing activities, or AOE - net for short .
In the AOE-network, there is a unique vertex with an in-degree of 0, called the source point ; there is a unique vertex with an out-degree of 0, called the sink . The length of the longest path from the source point to the sink point is the time required to complete the entire engineering task, and this path is called the critical path . Activities on the critical path are called critical activities . If any one of these activities is not completed on schedule, the completion time of the entire project will be delayed; on the contrary, if the progress of key activities can be accelerated, the entire project can be completed ahead of schedule.
AOE-NetFor example, in the AOE-net shown in the figure above, there are 9 events v0, v1, v2,..., v8. v0 is the source point and v8 is the sink point. There are two critical paths (longest paths) from v0 to v8: v0,v1,v4,v6,v8 or v0,v1,v4,v7,v8 , both of length 18. The key activities are a1, a4, a7, a10 or a1, a4, a8, a11. The key activity a1 is planned to be completed in 6 days. If a1 is completed two days in advance, the whole project can also be completed two days in advance.

Several basic definitions :
topological sorting see: https://blog.csdn.net/weixin_51450101/article/details/123018532?spm=1001.2014.3001.5501
①Earliest occurrence time ve(i) of event vi : from source point to vertex vi longest path length. To find ve(i), you can start from the source point and invert to the sink point
according to the topological order
: ve(0)=0;
ve(i)=Max{ve(k)+dut(<k,i>)}, dut (<k,i>) represents the duration of the activity corresponding to the arc <k,i>
The latest occurrence time vl(i) of time vi : On the premise of ensuring that the sink occurs at its earliest occurrence time, find The latest occurrence time of event vi. That is, on the basis of finding ve(i), start from the sink point and recurse to the source point in reverse topological order to find vl(i): vl(n-1)
=ve(n-1);
vl( i)=Min{vl(k)-dut(<i,k>)}, dut(<i,k>) indicates the duration of the activity corresponding to the arc <i,k> ③ The earliest start time
e ( i) : If the arc corresponding to activity ai is <j,k>, then e(i) is equal to the length of the longest path from source point to vertex j, ie e(i)=ve(j).
④The latest start time l(i) of activity ai : if the arc corresponding to activity ai is <j,k>, its duration is dut(<j,k>
Slack time (time margin) of activity ai : the difference between the latest start time and earliest start time of ai, ie l(i)-e(i). Obviously, an activity with a slack time of 0 is a key activity.

2. The basic process and calculation steps of finding the critical path

Basic process :
① Perform topological sorting on the vertices in the graph, and calculate the earliest occurrence time ve(i) of each event according to the reverse topological sequence during the sorting process; ② Calculate the latest occurrence time
vl(i) of each event according to the reverse topological sequence i);
③ Calculate the earliest start time ei(i) and the latest start time l(i) of each activity ai;
④ Find out that the activity ai with e(i)=l(i) is the key activity.
Calculation steps : Take
AOE-Netthe AOE-net shown in the figure above as an example:
① Calculate the earliest start time of each vertex
ve(0) = 0
ve(1) = Max {ve(0) + dut(<0,1>)} = 6
ve(2) = Max {ve(0) + dut(<0,2>)} = 4
ve(3) = Max {ve(0) + dut(<0,3>)} = 5
ve(4) = Max {ve(1) + dut(<1,4>), ve(2) + dut(<2,4>)} = 7 ve(5) = Max {ve(3) + dut(<
3, 5>)} = 7
ve(6) = Max {ve(4) + dut(<4,6>)} = 16
ve(7) = Max {ve(4) + dut(<4,7>), ve(5) + dut(<5,7>)} = 14
ve(8) = Max {ve(6) + dut(<6,8>), ve(7) + dut(<7,8>


vl(7) = Min{vl(8) - dut<7,8>} = 14
vl(6) = Min{vl(8) - dut<6,8>} = 16
vl(5) = Min{vl (7) - dut<5,7>} = 10
vl(4) = Min {vl(7) - dut<4,7>,vl(6) - dut(<4,6>)} = 7 vl
( 3) = Min {vl(5) - dut<3,5>} = 8
vl(2) = Min {vl(4) - dut<2,4>} = 6
vl(1) = Min {vl(4 ) - dut<1,4>} = 6
vl(0) ​​= Min {vl(3) - dut(<0,3>),vl(2) - dut<0,2>,vl(1) - dut (<0.1>)} = 0
③ Calculate the earliest start time of each activity
e(a1) = ve(0) = 0
e(a2) = ve(0) = 0
e(a3) = ve(0) = 0
e(a4) = ve(1) = 6
e(a5) = ve(2) = 4
e(a6) = ve(3) = 5
e(a7) = ve(4) = 7
e(a8) = ve(4) = 7
e(a9) = ve(5) = 7
e(a10) = ve(6) = 16
e(a11) = ve(7) = 14
④ Calculate the latest start time of each activity
l(a11) = vl(8) - dut(<7.8>) = 14
l(a10) = vl(8) - dut(<6.8>) = 16
l(a9) = vl(7) - dut(<5.7>) = 10
l(a8) = vl(7) - dut(<4.7>) = 7
l(a7) = vl(6) - dut(<4.6>) = 7
l(a6) = vl(5) - dut(< 3.5>) = 8
l(a5) = vl(4) - dut(<2.4>) = 6
l(a4) = vl(4) - dut(<1.4>) = 6
l(a3) = vl(3) - dut(<0.3>) = 3
l(a2) = vl(2) - dut(<0.2>) = 2
l(a1) = vl(1) - dut(<0.1>) = 0
⑤ Calculate the relaxation time of activity ai
l(a1) - e(a1) = 0
l(a2) - e(a2) = 2
l(a3) - e(a3) = 3
l(a4) - e( a4) = 0
l(a5) - e(a5) = 2
l(a6) - e(a6) = 3
l(a7) - e(a7) = 0
l(a8) - e(a8) = 0
l( a9) - e(a9) = 3
l(a10) - e(a10) = 0
l(a11) - e(a11) = 0
The activity with a slack time of 0 is the key activity, and the key activities obtained from the calculation result are: a1, a4, a7, a8, a10, a11 The key path is: v0, v1, v4, v6, v8
or v0, v1, v4, v7,v8

3. Implement code and run results

Find the inverse topological sequence and the earliest event time ve(i)

int indegree[MAX_VERTEX_NUM];			//存放各顶点入度数
int ve[MAX_VERTEX_NUM];					//各顶点的最早发生时间
void FindID(AdjList G);					//求各顶点入度算法

/*拓扑排序,求逆拓扑序列及事件最早发生时间ve(i)*/
int TopoSort(AdjList G, SeqStack* T) {
    
    	//栈T用于生成逆拓扑序列
	int i, k, count = 0;
	SeqStack S;							//栈S用于存放入度为0的顶点
	ArcNode* p;
	FindID(G);							//求各顶点入度
	InitStack(T);						//初始化栈T
	InitStack(&S);						//初始化栈S
	for (i = 0; i < G.vexnum; i++) {
    
    
		if (indegree[i] == 0)
			Push(&S, i);				//将入度为0的顶点入栈S
	}
	for (i = 0; i < G.vexnum; i++)
		ve[i] = 0;						//初始化最早发生时间
	while (!IsEmpty(&S)) {
    
    
		i = S.elem[S.top];
		Pop(&S);
		count++;
		Push(T, i);						//按拓扑顺序进入栈T
		p = G.vertex[i].firstarc;
		while (p != NULL) {
    
    
			k = p->adjvex;
			indegree[k]--;				//i号顶点的每个邻接点的入度减1
			if (indegree[k] == 0)
				Push(&S, k);			//若入度减为0则入栈
			if (ve[i] + p->weight > ve[k])	//按拓扑顺序计算事件最早发生时间
				ve[k] = ve[i] + p->weight;
			p = p->nextarc;
		}
	}
	if (count < G.vexnum) {
    
    
		printf("该图存在回路!\n");
		return FALSE;
	}
	else
		return TRUE;
}

critical path algorithm

/*关键路径算法*/
void CriticalPath(AdjList G, SeqStack* T) {
    
    
	ArcNode* p;
	int i, j, k, dut, ei, li;
	char tag;
	int vl[MAX_VERTEX_NUM];				//每个顶点的最迟发生时间
	TopoSort(G, T);						//① 求事件最早发生时间和逆拓扑序列栈T
	for (i = 0; i < G.vexnum; i++)
		vl[i] = ve[G.vexnum - 1];		//将各事件的最晚发生时间初始化为汇点的最早发生时间
	while (!IsEmpty(T)) {
    
    				//② 按逆拓扑顺序求各顶点的最晚发生时间vl值
		j = T->elem[T->top];
		Pop(T);
		p = G.vertex[j].firstarc;
		while (p != NULL) {
    
    
			k = p->adjvex;
			dut = p->weight;
			if (vl[k] - dut < vl[j])
				vl[j] = vl[k] - dut;
			p = p->nextarc;
		}
	}
	for (j = 0; j < G.vexnum; j++) {
    
    	//③ 求各活动的最早开始时间ei和最晚开始时间li
		p = G.vertex[j].firstarc;
		while (p != NULL) {
    
    
			k = p->adjvex;
			dut = p->weight;
			ei = ve[j];
			li = vl[k] - dut;
			if (ei == li)				//④ 输出关键活动及对应的路径
				printf("a%d v%c->v%c\n", p->activity, G.vertex[j].data, G.vertex[k].data);
			p = p->nextarc;
		}
	}
}

Complete implementation code

/*AOE-网的关键路径*/
# include<stdio.h>
# include<malloc.h>
# define MAX_VERTEX_NUM 20
# define TRUE 1
# define FALSE 0

/*AOE-网的邻接表表示法*/
typedef char VertexData;
//弧结点结构
typedef struct ArcNode {
    
    
	int adjvex;								//该弧指向顶点的位置
	struct ArcNode* nextarc;				//指向下一条弧的指针
	int activity;							//弧表示的活动
	int weight;								//权值
}ArcNode;
//表头结点结构
typedef struct VertexNode {
    
    
	VertexData data;						//顶点数据
	ArcNode* firstarc;						//指向该顶点的第一条弧的指针
}VertexNode;
//邻接表结构
typedef struct {
    
    
	VertexNode vertex[MAX_VERTEX_NUM];
	int vexnum, arcnum;						//图的顶点数和弧数
}AdjList;

/*求顶点位置*/
int LocateVertex(AdjList* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k].data == v)
			break;
	}
	return k;
}

/*创建AOE-网的邻接表*/
int CreateAdjList(AdjList* G) {
    
    
	int i, j, k, activity, weight;
	VertexData v1, v2;
	ArcNode* p;
	printf("输入图的顶点数和弧数:");			//输入图的顶点数和弧数
	scanf("%d%d", &G->vexnum, &G->arcnum);
	printf("输入图的顶点:");
	for (i = 0; i < G->vexnum; i++) {
    
    			//输入图的顶点,初始化顶点结点
		scanf(" %c", &(G->vertex[i].data));
		G->vertex[i].firstarc = NULL;
	}
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("输入第%d条弧的两个顶点、弧表示的活动及权值:", k + 1);
		scanf(" %c %c %d %d", &v1, &v2, &activity, &weight);	//输入一条弧的两个顶点、弧表示的活动及权值
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		p = (ArcNode*)malloc(sizeof(ArcNode));	//申请新弧结点
		p->activity = activity;
		p->weight = weight;
		p->adjvex = j;
		p->nextarc = G->vertex[i].firstarc;
		G->vertex[i].firstarc = p;
	}
}

/*顺序栈的存储结构*/
typedef struct {
    
    
	int elem[MAX_VERTEX_NUM];			//用于存放栈中元素的一维数组
	int top;							//存放栈顶元素的下标,top为-1表示空栈
}SeqStack;

/*初始化顺序栈*/
void InitStack(SeqStack* S) {
    
    
	S->top = -1;
}

/*判空*/
int IsEmpty(SeqStack* S) {
    
    
	if (S->top == -1)					//栈为空
		return TRUE;
	else
		return FALSE;
}

/*顺序栈进栈*/
int Push(SeqStack* S, int x) {
    
    
	if (S->top == MAX_VERTEX_NUM - 1)	//栈已满
		return FALSE;
	S->top++;
	S->elem[S->top] = x;				//x进栈
	return TRUE;
}

/*顺序栈出栈*/
int Pop(SeqStack* S) {
    
    
	if (S->top == -1)					//栈为空
		return FALSE;
	S->top--;
	return TRUE;
}

int indegree[MAX_VERTEX_NUM];			//存放各顶点入度数
int ve[MAX_VERTEX_NUM];					//各顶点的最早发生时间

/*求各顶点入度算法*/
void FindID(AdjList G) {
    
    
	int i;
	ArcNode* p;
	for (i = 0; i < G.vexnum; i++)
		indegree[i] = 0;
	for (i = 0; i < G.vexnum; i++) {
    
    
		p = G.vertex[i].firstarc;
		while (p != NULL) {
    
    
			indegree[p->adjvex]++;
			p = p->nextarc;
		}
	}
}

/*拓扑排序,求逆拓扑序列及事件最早发生时间ve(i)*/
int TopoSort(AdjList G, SeqStack* T) {
    
    	//栈T用于生成逆拓扑序列
	int i, k, count = 0;
	SeqStack S;							//栈S用于存放入度为0的顶点
	ArcNode* p;
	FindID(G);							//求各顶点入度
	InitStack(T);						//初始化栈T
	InitStack(&S);						//初始化栈S
	for (i = 0; i < G.vexnum; i++) {
    
    
		if (indegree[i] == 0)
			Push(&S, i);				//将入度为0的顶点入栈S
	}
	for (i = 0; i < G.vexnum; i++)
		ve[i] = 0;						//初始化最早发生时间
	while (!IsEmpty(&S)) {
    
    
		i = S.elem[S.top];
		Pop(&S);
		count++;
		Push(T, i);						//按拓扑顺序进入栈T
		p = G.vertex[i].firstarc;
		while (p != NULL) {
    
    
			k = p->adjvex;
			indegree[k]--;				//i号顶点的每个邻接点的入度减1
			if (indegree[k] == 0)
				Push(&S, k);			//若入度减为0则入栈
			if (ve[i] + p->weight > ve[k])	//按拓扑顺序计算事件最早发生时间
				ve[k] = ve[i] + p->weight;
			p = p->nextarc;
		}
	}

	printf("\n事件最早发生时间为:");	//输出事件的最早发生时间
	for (i = 0; i < G.vexnum; i++)
		printf("%d ", ve[i]);

	if (count < G.vexnum) {
    
    
		printf("该图存在回路!\n");
		return FALSE;
	}
	else
		return TRUE;
}

/*关键路径算法*/
void CriticalPath(AdjList G, SeqStack* T) {
    
    
	ArcNode* p;
	int i, j, k, dut, ei, li;
	char tag;
	int vl[MAX_VERTEX_NUM];				//每个顶点的最迟发生时间
	TopoSort(G, T);						//① 求事件最早发生时间和逆拓扑序列栈T
	for (i = 0; i < G.vexnum; i++)
		vl[i] = ve[G.vexnum - 1];		//将各事件的最晚发生时间初始化为汇点的最早发生时间
	while (!IsEmpty(T)) {
    
    				//② 按逆拓扑顺序求各顶点的最晚发生时间vl值
		j = T->elem[T->top];
		Pop(T);
		p = G.vertex[j].firstarc;
		while (p != NULL) {
    
    
			k = p->adjvex;
			dut = p->weight;
			if (vl[k] - dut < vl[j])
				vl[j] = vl[k] - dut;
			p = p->nextarc;
		}
	}

	printf("\n事件最晚发生时间为:");	//输出事件的最晚发生时间
	for (i = 0; i < G.vexnum; i++)
		printf("%d ", vl[i]);

	printf("\n关键活动及对应的弧为:\n");
	for (j = 0; j < G.vexnum; j++) {
    
    	//③ 求各活动的最早开始时间ei和最晚开始时间li
		p = G.vertex[j].firstarc;
		while (p != NULL) {
    
    
			k = p->adjvex;
			dut = p->weight;
			ei = ve[j];
			li = vl[k] - dut;
			if (ei == li)				//④ 输出关键活动及对应的路径
				printf("a%d v%c->v%c\n", p->activity, G.vertex[j].data, G.vertex[k].data);
			p = p->nextarc;
		}
	}
}

int main() {
    
    
	AdjList G;
	SeqStack T;
	CreateAdjList(&G);
	CriticalPath(G, &T);
	return 0;
}

Operation results
operation result
Reference: Geng Guohua "Data Structure - Described in C Language (Second Edition)"

For more data structure content, follow my "Data Structure" column : https://blog.csdn.net/weixin_51450101/category_11514538.html?spm=1001.2014.3001.5482

Guess you like

Origin blog.csdn.net/weixin_51450101/article/details/123044526