持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
借昨天的拓扑排序 今天对拓扑排序进行代码分析: 通过看和敲代码,对代码思路的总结(其中用到了拓扑排序的思想,一般对于AOE网,开始节点一定是入度为0的,结束节点一定是出度为0的) 大致思路:
-
1.算出各个节点的入度,找到入度为0的节点
-
2.从入度为0节点开始,到每个顶点的最早开始时间,并用数组记录(注意:有多个顶点汇聚到一个顶点,要找其中最大的那个最为最早开始时间。这就好比组装车,a组装发动机,b组装车身底盘,c组装车架,只有三者都完成,才能开始进行d 组装整个车,而d最早开始时间要看a,b,c中最晚时间)
-
3.算出各个节点的出度,找到出度为0的节点,
-
4.从出度为0的节点开始,到每个顶点的最晚开始时间,在拓扑排序是正着找入度为0的就移除掉这个顶点,接着再找入度为0的,再这里我们可以将拓扑排序反着来,找出度为0再移除,这样我们就能去找最晚时间。节点最晚时间是看这个节点所有的后继节点到终点最长的的时间那个。
在代码中,找出度和入度为0的双层循环,只是赋值的时候是i还是j有区别,我觉得这个就挺简单明了的。其次就是在求各个节点最迟发生时间时的初始化,需要注意的,要看最后一个出度为0的最早发生时间作为初始值(我认为主要是我们求最迟发生时间要反着看才容易推出最早时间)
在求节点最早和最晚时间时,用拓扑排序思想(正反)去求解这个思路学习到了。
public boolean[] criticalPath() {
// One more value to save simple computation.
int tempValue;
// Step 1. The in-degree of each node. 每个顶点的入度个数
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]++;
}
}
}
System.out.println("In-degree of nodes: " + Arrays.toString(tempInDegrees));
// Step 2. Topology sorting. 到每个顶点最短时间 = 多个汇点中最长的那个值
int[] tempEarliestTimeArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
//拓扑排序最考试的是找到入度为0的点
if (tempInDegrees[i] > 0) {
continue;
}
System.out.println(("Removing " + i));
//判断入度为0的点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;
}
tempInDegrees[j]--;
}
}
}
System.out.println("Earlest start time: " + Arrays.toString(tempEarliestTimeArray));
//Step3 The out-degree of each node. 每个节点到其他节点的出度(包含自身)
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]++;
}
}
}
System.out.println("Out-degree of nodes: " + Arrays.toString(tempOutDegrees));
// Step 4. Reverse topology sorting. 这里初始化每个节点最迟发生时间
int[] tempLatestTimeArray = new int[numNodes];
for (int i = 0; i < numNodes; i++) {
tempLatestTimeArray[i] = tempEarliestTimeArray[numNodes - 1];
}
//看节点最迟发生的时间 需要倒着看更容易理解
for (int i = numNodes - 1; i >= 0; i--) {
// This node cannot be removed. 一般出度为0的是最后一个节点
if (tempOutDegrees[i] > 0) {
continue;
}
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;
}
tempOutDegrees[j]--;
System.out.println("The out-degree of " + j + " decreases by 1.");
}
}// 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;
}
总结
今天学习了关键路径的代码实现,在这个过程中,利用拓扑排序思想去实现,以前只会正着拓扑排序,在这里我们也可以倒着拓扑排序,这个思维感觉很灵活,又学习到了!同时我们在求关键路径时,可以只通过节点就可以求出关键路径上的节点,进而我们就可以确定关键路径。