前面拓扑排序中,说到了AOV网。拓扑排序:https://mp.csdn.net/postedit/91047889
在关键路径中,在AOV网的基础上加了一点点新的特点,我们称之为
AOE网,所谓AOE网就是在一个表示工程的带权有向图中,用顶点表示事件,用边上的权值表示活动的持续时间,
这种有向图的边表示活动的网,称为AOE网。
与AOV不同,AOV是顶点表示活动,边表示优先关系
注意:AOE网中只有一个源点(起点)和一个汇点(终点)
求最早开始时间:从源点到汇点求,每次找出最大值
求最晚开始时间:从汇点到源点求,每次找出最小值
1.数据结构
/**
* 顶点:在关键路径中表示事件
*/
public class Vertex {
private int in; //入度,用于拓扑排序入栈
private String data; //顶点数据域,在此只是顶点名称 可专门自定义类存放顶点信息
private Edge firstEdge; //与该顶点相连的第一条边
//省略set,get方法}
/**
* 边: 在关键路径中表示活动
*/
public class Edge {
private int adjvex; //该边终点在顶点数组中的序号
private int weight; //权值, 在此表示活动持续时间
private Edge next; //起点相同,终点不同的边的下一个顶点
//省略set,get方法}
图的数据结构:
public class GraphList {
private Vertex[] vertexs = new Vertex[100]; //最多存放100顶点 可自定义
private int vertexNum; //存放的顶点数
private int edgeNum; //存放的边数
//省略get,set方法}
2.实现与测试
public class App {
public static void main(String[] args){
GraphList g = createGraph();
// traverseGraph(g);
criticalPath(g);
}
/**
* 使用邻接表创建图
*/
private static GraphList createGraph(){
GraphList list = new GraphList();
Scanner sc = new Scanner(System.in);
System.out.println("输入顶点数:");
int vertextNum = sc.nextInt();
list.setVertexNum(vertextNum);
System.out.println("输入边数:");
int edgeNum = sc.nextInt();
list.setEdgeNum(edgeNum);
for(int i = 0; i < list.getVertexNum(); i++){
Vertex vertexNode = new Vertex();
vertexNode.setData("v" + i); //顶点名称 v0,v1,....
list.getVertexs()[i] = vertexNode;
}
//起点,终点的位置必须小于顶点数
for(int i = 0; i < list.getEdgeNum(); i++){
System.out.println("请输入边(vi,vj)的起点:");
int start = sc.nextInt();
System.out.println("请输入边(vi,vj)的终点:");
int end = sc.nextInt();
System.out.println("请输入边(vi,vj)权值:");
int weight = sc.nextInt();
Edge edgeNode = new Edge();
edgeNode.setAdjvex(end);
edgeNode.setWeight(weight);
Vertex vertexNode = list.getVertexs()[end];
vertexNode.setIn(vertexNode.getIn() + 1); //给终点对应的顶点入度 +1
//将边 按照头插法 插入到链表中
edgeNode.setNext(list.getVertexs()[start].getFirstEdge());
list.getVertexs()[start].setFirstEdge(edgeNode);
}
return list;
}
private static void traverseGraph(GraphList g){
for(int i = 0; i < g.getVertexNum(); i++){
System.out.println("顶点v" + i + ":");
Edge edgeNode = g.getVertexs()[i].getFirstEdge();
while(edgeNode != null){
System.out.print("v" + edgeNode.getAdjvex() + "权值" + edgeNode.getWeight() + "->");
edgeNode = edgeNode.getNext();
}
System.out.println();
}
}
private static void criticalPath(GraphList g){
int[] etv = new int[g.getVertexNum()]; //存放顶点(事件)的最早开始时间
int[] ltv = new int[g.getVertexNum()]; //存放顶点(事件)的最晚开始时间
int[] stack2 = new int[g.getVertexNum()]; //存放stack1出栈的元素,用于求事件最晚开始时间
int top2 = 0; //用于指向stack2的栈尾
int count = 0; //用于判断是否是AOV网
int[] stack1 = new int[g.getVertexNum()]; //用于存放入栈的顶点在数组中的序号 可自定义 也可以建立Vertex[]数组
int top1 = 0; //用于指向stack的栈尾
for(int i = 0; i < g.getVertexNum(); i++){
if(g.getVertexs()[i].getIn() == 0){ //如果入度为0 入栈
stack1[top1++] = i;
}
//初始化最早开始时间
etv[i] = 0;
}
//使用拓扑排序求顶点最早开始时间 etv -----start
//从源点开始(关键路径的第一个顶点)
while(top1 != 0){ //如果不是空栈
int getTop = stack1[--top1];
count ++;
stack2[top2++] = getTop;
Edge edge = g.getVertexs()[getTop].getFirstEdge();
while(edge != null){
//使边(vi,vj)的终点对应的顶点入度减1
Vertex vertex = g.getVertexs()[edge.getAdjvex()];
vertex.setIn(vertex.getIn() -1);
//如果该顶点入度为0 则入栈
if(vertex.getIn() == 0){
stack1[top1 ++] = edge.getAdjvex();
}
//如果vi的最早开始时间+ 边(vi,vj)的权值 > vj的最早开始时间 取最大值
if(etv[getTop] + edge.getWeight() > etv[edge.getAdjvex()]){
etv[edge.getAdjvex()] = etv[getTop] + edge.getWeight();
}
edge = edge.getNext();
}
}
if(count != g.getVertexNum()){
System.out.println("路径有环,不是AOV网");
return;
}
//使用拓扑排序求顶点最早开始时间 ----end
//求最晚开始时间 ---start
//初始化最晚开始时间
for(int i = 0; i < g.getVertexNum(); i++){
ltv[i] = etv[g.getVertexNum()-1]; //初始化为最早开始时间的最大值(关键路径的时间之和)
}
//从汇点(关键路径最后一个顶点)开始
while(top2 != 0){
int getTop = stack2[--top2];
Edge edge = g.getVertexs()[getTop].getFirstEdge();
while(edge != null){ //汇点的edge为null
//如果vj顶点最晚开始时间 - 边(vi,vj)的权值 < vi顶点的最晚开始时间
if(ltv[edge.getAdjvex()] - edge.getWeight() < ltv[getTop]){
// ltv[edge.getAdjvex()] = ltv[getTop] - edge.getWeight();
ltv[getTop] = ltv[edge.getAdjvex()] - edge.getWeight();
}
edge = edge.getNext();
}
}
//顶点最晚开始时间 --end
//判断是不是关键活动的条件: 关键活动的最早开始时间 = 关键活动的最晚开始时间
int ete;
int lte;
System.out.println("关键路径:");
for(int i = 0; i < g.getVertexNum(); i++){
Edge edge = g.getVertexs()[i].getFirstEdge();
while(edge != null){
ete = etv[i]; //边的最早开始时间 就是 顶点的最早开始时间
lte = ltv[edge.getAdjvex()] - edge.getWeight();
if(ete == lte){
System.out.print("(v" + i + ",v" + edge.getAdjvex() + "),权值:" + edge.getWeight() + "->");
}
edge = edge.getNext();
}
}
}
}
3.测试数据
V{v0,v1,v2,v3,v4,v5,v6,v7,v8,v9}
E{(v0,v1,3),(v0,v2,4),(v1,v3,5),(v2,v3,8),(v1,v4,6),(v3,v4,3),(v2,v5,7),(v4,v6,9),(v4,v7,4),(v5,v7,6),(v6,v9,2),(v7,v8,5),(v8,v9,3)}
4.测试结果
关键路径:
(v0,v2),权值:4->(v2,v3),权值:8->(v3,v4),权值:3->(v4,v7),权值:4->(v7,v8),权值:5->(v8,v9),权值:3->