赞
踩
具体内容:
该文章的源代码仓库为:https://github.com/MeteorCh/DataStructure/blob/master/Java/DataStructure/src/Graph/AdjListGraph.java
如何实现拓扑排序呢?具体步骤如下:
我在以前实现的邻接表存储的图(博客在这里,源码在这里)来实现拓扑排序,我这里只写函数,具体的图的具体Java代码如下:
首先是拓扑排序的实现代码,函数参数earlistStartTime是在求解关键路径时,记录事件的最早开始时间的,在这个函数里有关earlistStartTime的代码暂时先不用管。
/** * 拓扑排序 * @param earlistStartTime 事件的最早开始时间 * @return 拓扑排序结果 */ protected LinkedList<Integer> topoSort(int[] earlistStartTime) throws Exception { int[] in=new int[vertexNum];//统计每个节点的入度的数组 boolean[] visitFlag=new boolean[vertexNum];//标记节点是否已输出 //初始化 if (earlistStartTime!=null){ for (int i=0;i<vertexNum;i++) earlistStartTime[i]=0; } for (int i=0;i<vertexNum;i++){ visitFlag[i]=false; VertexNode node=vertexes[i]; ArcNode arcNode=node.firstArc; while (arcNode!=null){ in[arcNode.nodeIndex]++; arcNode=arcNode.next; } } LinkedList<Integer> stack=new LinkedList<>(); LinkedList<Integer> result=new LinkedList<>(); //将入度为0的下标入栈 for (int i=0;i<vertexNum;i++) if (in[i]==0) { stack.add(in[i]); visitFlag[i]=true; } //开始拓扑排序 while (!stack.isEmpty()){ //弹栈 int curIndex=stack.pop(); result.push(curIndex); visitFlag[curIndex]=true; //将curIndex相连的节点的入度减1 ArcNode curArc=vertexes[curIndex].firstArc; while (curArc!=null){ if (!visitFlag[curArc.nodeIndex]) { in[curArc.nodeIndex]--; //需要计算事件的最早开始时间 if (earlistStartTime!=null){ if (curArc.weight instanceof Integer){ Integer weight=(Integer)curArc.weight; if (earlistStartTime[curIndex]+weight>earlistStartTime[curArc.nodeIndex]) earlistStartTime[curArc.nodeIndex]=earlistStartTime[curIndex]+weight; }else throw new Exception("暂时只支持整形权重"); } //如果入度变为0,则入栈 if (in[curArc.nodeIndex]==0){ stack.add(curArc.nodeIndex); visitFlag[curArc.nodeIndex]=true; } } curArc=curArc.next; } } return result; }
在上面的代码里,用in这个数组来存储顶点的入度(因为邻接表求解入度不方便,所以在程序开始的时候先通过遍历图来求解每个顶点的入度并记录在数组中)。然后上面主要是用了栈实现拓扑排序的,代码比较简单,我就不细说了。还有一个就是,为了支持多种数据类型,我使用了泛型了表示权重,这个泛型继承Numbers,结果发现在比较大小的时候,泛型是个大坑。。。然后没办法,在这里我就只能对权重进行强制规定成整形的(这里可能是我的知识储备不够,如果有同学知道这种情况应该怎么解决,可以在评论区跟我说一下)。
然后是提供的供外界调用的函数:
/** * 拓扑排序 */ public void topoSort(){ try { LinkedList<Integer> result=topoSort(null); if (result.size()==vertexNum) { System.out.print("该图的拓扑排序结果为:"); for (int i:result) System.out.print(vertexes[i].data+","); System.out.println(); } else System.out.println("图中存在环,不存在拓扑排序"); }catch (Exception e){ System.out.println("目前拓扑排序只支持整形权重"); } }
可以看到这里对图中是否存在环做了一个判断。
以最上面的那个图为例,对其进行拓扑排序,调用代码为
AdjListGraph graph=AdjListGraph.getTestInstance();
System.out.println(graph.toString());
graph.topoSort();
上面的AdjListGraph.getTestInstance函数就是生成最上面的示例图,输出结果为:
首先做一些定义:
根据上面的计算方法,将每个活动的最早开始和最晚开始时间计算如下:
==其中最早开始时间和最晚开始时间相等的活动为关键活动,==那关键活动为V0V1,V1V2,V2V3,V3V4,关键路径为V0->V1->V2->V3->V4
我在以前实现的邻接表存储的图(博客在这里,源码在这里)来实现拓扑排序,我这里只写函数,具体的图的具体Java代码如下:
public void keyPath(){ try{ int[] etv=new int[vertexNum];//事件的最早发生时间 LinkedList<Integer> topoResult=topoSort(etv);//拓扑排序结果 if (topoResult.size()==vertexNum){ int[] ltv=new int[vertexNum];//事件的最晚发生时间 //初始化ltv for (int i=0;i<vertexNum;i++) ltv[i]=etv[vertexNum-1]; //求解ltv while (!topoResult.isEmpty()){ int curIndex=topoResult.pop(); ArcNode curArc=vertexes[curIndex].firstArc; while (curArc!=null){ Integer weight=(Integer)curArc.weight; if (ltv[curArc.nodeIndex]-weight<ltv[curIndex]) ltv[curIndex]=ltv[curArc.nodeIndex]-weight; curArc=curArc.next; } } //求解每个活动的最早开始时间和最晚开始时间 System.out.print("AOE网中的关键活动为:"); for (int i=0;i<vertexNum;i++){ ArcNode curArc=vertexes[i].firstArc; while (curArc!=null){ int ete=etv[i];//活动的最早开始时间 int lte=ltv[curArc.nodeIndex]-(Integer) curArc.weight;//活动的最晚开始时间 if (ete==lte) System.out.print(vertexes[i].data.toString()+vertexes[curArc.nodeIndex].data+","); curArc=curArc.next; } } System.out.println(); }else { System.out.println("图中存在环!"); } }catch (Exception e){ System.out.println("目前关键路径只支持整形权重"); } }
上面的代码中,先调用开始讲的拓扑排序获得图的拓扑排序和每个事件的最早发生时间,然后再逆推得到每个时间的最晚发生时间,再遍历边得到每个活动的最早和最晚开始时间,判断是否相等来判断活动是否为关键活动。
调用下面的代码,通过getTestInstance创建上面的AOE网,然后计算关键路径
AdjListGraph graph=AdjListGraph.getTestInstance();
System.out.println(graph.toString());
graph.keyPath();
计算结果输出为:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。