关键路径

前面拓扑排序中,说到了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->

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值