携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情
AOE网
在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,我们称之为AOE网。
特点
- 有向无环图
- 拓扑排序
- 没有入度的顶点称为始点或源点。
- 没有出度的顶点称为终点或汇点。
- 关键路径,就是求最大的路径。
- 一般是一个开始点,一个结束点。
排序算法
算法的结果就是找到关键路径
- etv(Earliest Time Of Vertex) 事件最早发生时间,顶点最早发生时间。
- ltv(Latest Time Of Vertex) 事件最晚发生时间,顶点最晚发生时间。
- ete(Earliest Time Of Edge) 活动最早开始时间,边最早开始时间。
- lte(Latest Time Of Edge) 活动最晚开始时间,边最晚开始时间。
代码实现
import java.util.ArrayList;
import java.util.List;
/**
* author: arrom
* email: 1803935398@qq.com
*/
public class AOE<T> {
//根据上面的图来定义数组的长度
int LENGHT = 9;
// etv(Earliest Time Of Vertex) 事件最早发生时间,顶点最早发生时间
int[] etv = new int[LENGHT];
// ltv(Latest Time Of Vertex) 事件最晚发生时间,顶点最晚发生时间
int[] ltv = new int[LENGHT];
// ete(Earliest Time Of Edge) 活动最早开始时间,边最早开始时间
int[] ete = new int[LENGHT];
// lte(Latest Time Of Edge) 活动最晚开始时间,边最晚开始时间
int[] lte = new int[LENGHT];
int[] stack2 = new int[LENGHT];
int top2 = 0;
/**
* 拓扑排序算法
*
* @param graphAdjList 拓扑图数组集
* @return
*/
public List<T> topologicalSort(VertexNode[] graphAdjList) {
int top = 0; //栈顶指针, 对应索引值
int[] stack = new int[LENGHT];//用来存放入度为0的顶点,数组效率最高,存放顶点的下标索引值
//循环得到入度为0的所有顶点
for (int i = 0; i < graphAdjList.length; i++) {
VertexNode vertexNode = graphAdjList[i];
if (vertexNode.in == 0) {
stack[++top] = i;
}
}
//开始算法的逻辑
List<T> resultList = new ArrayList<>();
while (top != 0) {
int getTop = stack[top--];
resultList.add((T) graphAdjList[getTop].data);
//保存拓扑序列顺序
stack2[top2++] = getTop;
//更新当前输出节点所有的出边(后继顶点)
for (EdgeNode e = graphAdjList[getTop].first; e != null; e = e.next) {
int index = e.index;
//入度减一
graphAdjList[index].in--;
if (graphAdjList[index].in == 0) {
stack[++top] = index;
}
// 1. 计算顶点的最早开始时间
if(etv[index] < (etv[getTop] + e.weight)){
etv[index] = etv[getTop] + e.weight;
}
}
}
return resultList;
}
/**
* 关键路径算法
*/
public void criticalPath(VertexNode[] graphAdjList){
// List<T> topologicalSortList = topologicalSort(graphAdjList);
//初始化顶点最晚发生时间(ltv)都为汇点时间
for (int i = 0; i < LENGHT; i++) {
ltv[i] = etv[LENGHT - 1];
}
//从汇点开始倒过来计算 顶点的最晚开始时间(ltv)
while (top2 > 0) {
int getTop = stack2[--top2];//从汇点开始
for (EdgeNode e = graphAdjList[getTop].first; e != null; e = e.next) {
int index = e.index;
// 2. 计算顶点的最晚开始时间
if(ltv[index] - e.weight < ltv[getTop]){
ltv[getTop] = ltv[index] - e.weight;
}
}
}
//通过 etv 和 ltv 计算出ete 和 lte
for (int i = 0; i < LENGHT; i++) {
for (EdgeNode e = graphAdjList[i].first; e != null; e = e.next) {
int index = e.index;
ete[i] = etv[i];// 3. 边最早的时间 ete,就是顶点最早时间 etv
lte[i] = ltv[index] - e.weight; // 4. 计算边最晚发生时间,ltv[index]里面已经是选的最小的权重
}
}
}
/**
* 边表节点
*/
class EdgeNode {
int index; //用来存放顶点的下标索引值
int weight; //存放边的权重值
EdgeNode next;
public EdgeNode(int index, EdgeNode next) {
this.index = index;
this.next = next;
}
public EdgeNode(int index, int weight, EdgeNode next) {
this.index = index;
this.weight = weight;
this.next = next;
}
}
/**
* 顶点表节点
*/
class VertexNode<T> {
int in;//入度
T data;
EdgeNode first;
public VertexNode(int in, T data, EdgeNode first) {
this.in = in;
this.data = data;
this.first = first;
}
}
}