开始之前先给自己打个鸡血吧:
A journey of a thousand miles begins with a single step. 千里之行始于足下。
本人没有系统学过Java,数据结构基本算法掌握也不够好。现跟随闵老师的《日撸 Java 三百行》,从基础到算法再到机器学习部分,开始我的漫漫修炼之路。希望自己能够坚持并且最终学有所获。
附上老师课程链接:https://blog.csdn.net/minfanphd/article/details/116974889
关键路径
因为昨天刚好对数据结构中图的关键路径这个知识点进行了复习,所以第一天就准备就从这开始。
要搞清楚关键路径,不得不提到图的相关内容以及概念:AOE-网(Activity ON Edge)、拓扑排序等等。
-
AOE-网(Activity ON Edge)
用顶点表示事件,弧表示活动,弧上的权值表示活动持续的时间,AOE-网是表示工程流程的。
性质:
- 只有在顶点所代表的事件发生后,从该顶点出发的各活动才能开始,并且是可以同时进行的。
- 只有在进入某指向某个顶点的各活动都结束,该顶点所代表的事件才能发生。
-
拓扑排序
是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且必须满足下面两个条件:
- 每个顶点出现且只出现一次。
- 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
拓扑排序的思想是:
- 选取一个没有前驱的顶点,输出。
- 删去此顶点以及所有以它为尾的弧。
-
最早发生时间
顶点的最早发生时间,即从源点到这个顶点的最长路径。
这儿为什么是最长而不是最短路径,我也理解了很久。一开始理解的最早发生时间就是字面上的最早,我浅显的认为要得到最早发生时间就应该走最短路径,实际上根本不是。回顾AOE网的性质不难理解,顶点事件的最早发生时间,意味着指向它的活动都全部完成了,而指向它的活动可能有多个,那么这个事件要发生,就需要这多个活动中耗时最长的活动完成之后才能发生,即最长路径。
如下图所示,指向事件4的活动有两个,路径分别是1—>3—>4和1—>2—>4,完成时间分别为6和5。最早发生时间应当保证这两条路上活动都完成之后才能开始,即最长路径也就是6。
-
最晚发生时间
在不推迟整个工程完成的前提下,保证后继事件能在其最迟发生时间能够发生时,该事件最迟必须发生的时间。
单看最晚发生时间概念可能也有些绕,我的理解是:顶点事件指向的活动也可能有多个,要保证不得耽误它的下一个事件,那么这个事件最晚发生时间就必须从下一个事件的最晚发生时间减去多条路径对应活动时间得到的值中取最小值才行。
如下图所示,事件2完成之后事件4、5才能发生,如果要不耽误4、5的完成时间,那么事件2的最晚发生时间必然需要在4和5的最晚发生时间减去相应活动的时间,取最小值。
-
关键路径
有了以上概念,那么对于关键路径就不难理解了。
关键路径就是从源点到汇点的最长路径,即该路径的各个活动所持续的时间之和最大。关键路径上的活动没有推迟的余地,所以其最早发生时间和最晚发生时间应该是相等的。
代码分析及实现
搞懂了以上概念,直接开始上代码。老师的代码写的十分清楚,格式也非常规范,我认为代码中找关键路径的核心步骤主要有三个:
- 拓扑排序——>事件的最早发生时间
- 逆拓扑排序——>事件的最晚发生时间
- 比较上述两个时间是否相等,相等则事件为关键路径,反之则不是
/**
*********************
* Critical path. Net validity checks such as loop check not implemented.
* The source should be 0 and the destination should be n-1.
*
* @return The node sequence of the path.
*********************
*/
public boolean[] criticalPath() {
// One more value to save simple computation.
int tempValue;
// 入度
int[] tempInDegrees = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
for (int j = 0; j < numNodes; j++) {
if (weightMatrix.getValue(i, j) != -1) {
tempInDegrees[j]++;
} // Of if
} // Of for j
} // Of for i
System.out.println("In-degree of nodes: " + Arrays.toString(tempInDegrees));
// 拓扑排序
int[] tempEarliestTimeArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
// This node cannot be removed.
if (tempInDegrees[i] > 0) {
continue;
} // Of if
System.out.println("Removing " + i);
for (int j = 0; j < numNodes; j++) {
if (weightMatrix.getValue(i, j) != -1) {
tempValue = tempEarliestTimeArray[i] + weightMatrix.getValue(i, j);
if (tempEarliestTimeArray[j] < tempValue) {
tempEarliestTimeArray[j] = tempValue;
} // Of if
//拓扑排序第二步,删除顶点以及所有以它为尾的弧,即顶点入度减1
tempInDegrees[j]--;
} // Of if
} // Of for j
} // Of for i
System.out.println("Earlest start time: " + Arrays.toString(tempEarliestTimeArray));
// 出度
int[] tempOutDegrees = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
for (int j = 0; j < numNodes; j++) {
if (weightMatrix.getValue(i, j) != -1) {
tempOutDegrees[i]++;
} // Of if
} // Of for j
} // Of for i
System.out.println("Out-degree of nodes: " + Arrays.toString(tempOutDegrees));
// 逆拓扑排序
int[] tempLatestTimeArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
tempLatestTimeArray[i] = tempEarliestTimeArray[numNodes - 1];
} // Of for i
for (int i = numNodes - 1; i >= 0; i--) {
// This node cannot be removed.
if (tempOutDegrees[i] > 0) {
continue;
} // Of if
System.out.println("Removing " + i);
for (int j = 0; j < numNodes; j++) {
if (weightMatrix.getValue(j, i) != -1) {
tempValue = tempLatestTimeArray[i] - weightMatrix.getValue(j, i);
if (tempLatestTimeArray[j] > tempValue) {
tempLatestTimeArray[j] = tempValue;
} // Of if
tempOutDegrees[j]--;
System.out.println("The out-degree of " + j + " decreases by 1.");
} // Of if
} // Of for j
} // Of for i
System.out.println("Latest start time: " + Arrays.toString(tempLatestTimeArray));
//比较两个时间
boolean[] resultCriticalArray = new boolean[numNodes];
for (int i = 0; i < numNodes; i++) {
if (tempEarliestTimeArray[i] == tempLatestTimeArray[i]) {
resultCriticalArray[i] = true;
} // Of if
} // Of for i
System.out.println("Critical array: " + Arrays.toString(resultCriticalArray));
System.out.print("Critical nodes: ");
for (int i = 0; i < numNodes; i++) {
if (resultCriticalArray[i]) {
System.out.print(" " + i);
} // Of if
} // Of for i
System.out.println();
return resultCriticalArray;
}// Of criticalPath
/**
*********************
* The entrance of the program.
*
* @param args
* Not used now.
*********************
*/
public static void main(String args[]) {
Net tempNet0 = new Net(3);
System.out.println(tempNet0);
int[][] tempMatrix1 = { { 0, 9, 3, 6 }, { 5, 0, 2, 4 }, { 3, 2, 0, 1 }, { 2, 8, 7, 0 } };
Net tempNet1 = new Net(tempMatrix1);
System.out.println(tempNet1);
// Dijkstra
tempNet1.dijkstra(1);
// An undirected net is required.
int[][] tempMatrix2 = { { 0, 7, MAX_DISTANCE, 5, MAX_DISTANCE }, { 7, 0, 8, 9, 7 },
{ MAX_DISTANCE, 8, 0, MAX_DISTANCE, 5 }, { 5, 9, MAX_DISTANCE, 0, 15, },
{ MAX_DISTANCE, 7, 5, 15, 0 } };
Net tempNet2 = new Net(tempMatrix2);
tempNet2.prim();
// A directed net without loop is required.
// Node cannot reach itself. It is indicated by -1.
//创建图
int[][] tempMatrix3 = { { -1, 3, 2, -1, -1, -1 }, { -1, -1, -1, 2, 3, -1 },
{ -1, -1, -1, 4, -1, 3 }, { -1, -1, -1, -1, -1, 2 }, { -1, -1, -1, -1, -1, 1 },
{ -1, -1, -1, -1, -1, -1 } };
Net tempNet3 = new Net(tempMatrix3);
System.out.println("-------critical path");
tempNet3.criticalPath();
}// Of main
运行结果截图:
总结
关键路径属于数据结构图这个板块比较重要的部分,其建立在活动之间制约关系没有矛盾的基础之上(无环)。可以通过得到完成整个工程需要多少时间,来分析出应当加快哪些活动等问题。在今天的学习中耗时最久的是在概念的理解上,只要理解到位了,看代码也顺眼多了。