有向图的拓补排序算法
拓扑排序算法介绍
拓扑排序解决的是一系列相互依赖的事件的排序问题,比如Ant中有很多的Task,而某些Task依赖于另外的Task,编译之前需要清理空间,打包之前要先编译,但其它一些Task处理顺序可以调换(是无所谓前后,不是并行), 如何安排Task的执行顺序就可以用拓扑排序解决。熟悉Java的朋友应该都知道Spring,一个非常优秀的解决组件(Bean)依赖的框架,组件之间可能有依赖关系,也可能没有关系,如何按顺序创建组件也是相同的问题。本文使用的是图搜索算法里面的深度优先排序算法解决问题。需要特别指出的是拓扑排序算法的结果很可能有多个(不依赖的可以调换位置),而算法也可以有多种,深度优先排序算法只是其中一种而已。拓扑排序为线性排序,效率为O(|V|+|E|),其中|V|表示顶点数,|E|表示边的数量。
Java实现有向图的拓补排序:
思路:关于有向图的拓补排序:
首先我们要确定好一个规则,就是一个顶点他的那些入度对应的顶点全部用完,才可以轮到该顶点自己!
如图:
例如v2,他有俩入度,那么要想使用v2,就必须等待v1和v3执行完!
首先我们建立一个栈Stack,把v0,v1,v3压栈;
然后拿出v3,打印v3,看看v3有哪些出度对应的顶点(这里是v2和v13),看看这俩顶点除了v3这个入度外还有没有其他的入度,有的话不能压栈。。
以此类推。。。
代码实现如下:
public class DnGraphTopologic { private int numVertexes; private VertexNode[] adjList;// 邻接顶点的一维数组 public DnGraphTopologic(int numVertexes) { this.numVertexes = numVertexes; } private void createGraph() { VertexNode node0 = new VertexNode(0, "v0"); VertexNode node1 = new VertexNode(0, "v1"); VertexNode node2 = new VertexNode(2, "v2"); VertexNode node3 = new VertexNode(0, "v3"); VertexNode node4 = new VertexNode(2, "v4"); VertexNode node5 = new VertexNode(3, "v5"); VertexNode node6 = new VertexNode(1, "v6"); VertexNode node7 = new VertexNode(2, "v7"); VertexNode node8 = new VertexNode(2, "v8"); VertexNode node9 = new VertexNode(1, "v9"); VertexNode node10 = new VertexNode(1, "v10"); VertexNode node11 = new VertexNode(2, "v11"); VertexNode node12 = new VertexNode(1, "v12"); VertexNode node13 = new VertexNode(2, "v13"); adjList = new VertexNode[numVertexes]; adjList[0] = node0; adjList[1] = node1; adjList[2] = node2; adjList[3] = node3; adjList[4] = node4; adjList[5] = node5; adjList[6] = node6; adjList[7] = node7; adjList[8] = node8; adjList[9] = node9; adjList[10] = node10; adjList[11] = node11; adjList[12] = node12; adjList[13] = node13; node0.firstEdge = new EdgeNode(11); node0.firstEdge.next = new EdgeNode(5); node0.firstEdge.next.next = new EdgeNode(4); node1.firstEdge = new EdgeNode(8); node1.firstEdge.next = new EdgeNode(4); node1.firstEdge.next.next = new EdgeNode(2); node2.firstEdge = new EdgeNode(9); node2.firstEdge.next = new EdgeNode(6); node2.firstEdge.next.next = new EdgeNode(5); node3.firstEdge = new EdgeNode(13); node3.firstEdge.next = new EdgeNode(2); node4.firstEdge = new EdgeNode(7); node5.firstEdge = new EdgeNode(12); node5.firstEdge.next = new EdgeNode(8); node6.firstEdge = new EdgeNode(5); node8.firstEdge = new EdgeNode(7); node9.firstEdge = new EdgeNode(11); node9.firstEdge.next = new EdgeNode(10); node10.firstEdge = new EdgeNode(13); node12.firstEdge = new EdgeNode(9); EdgeNode n11 = new EdgeNode(11); EdgeNode n5 = new EdgeNode(5); EdgeNode n4 = new EdgeNode(4); EdgeNode n8 = new EdgeNode(8); EdgeNode n2 = new EdgeNode(2); EdgeNode n9 = new EdgeNode(9); EdgeNode n6 = new EdgeNode(6); EdgeNode n13 = new EdgeNode(13); EdgeNode n7 = new EdgeNode(7); EdgeNode n12 = new EdgeNode(12); EdgeNode n10 = new EdgeNode(10); /* * 为啥下面不可以同一个n5多个共用?因为公用的话,会出现上面n5还有next,到了下面n5明明不应该有,但是也还是有了, * 当时我还想,如果不是同一个对象的话,那出度会不会受影响,但是我想多了,因为出度不是由n5决定的, * 而是由adjlist来决定的,这个倒是只创建了一个对象。 */ // node0.firstEdge = n11; // node0.firstEdge.next = n5; // node0.firstEdge.next.next = n4; // node1.firstEdge = n8; // node1.firstEdge.next = n4; // node1.firstEdge.next.next = n2; // node2.firstEdge = n9; // node2.firstEdge.next = n6; // node2.firstEdge.next.next = n5; // node3.firstEdge = n13; // node3.firstEdge.next = n2; // node4.firstEdge = n7; // node5.firstEdge = n12; // node5.firstEdge.next = n8; // node6.firstEdge = n5; // node8.firstEdge = n7; // node9.firstEdge = n11; // node9.firstEdge.next = n10; // node10.firstEdge = n13; // node12.firstEdge = n9; } /** * 拓扑排序 * * @author Administrator * @throws Exception * */ private void topologicalSort() throws Exception { Stack<Integer> stack = new Stack<>(); int count = 0; int k = 0; // 开头就先把入度为0的vertexNode压栈(如果没有,就代表有回环) for (int i = 0; i < numVertexes; i++) { if (adjList[i].in == 0) { stack.push(i); } } while (stack.size() > 0) { // 取出栈中的下标值 int pop = stack.pop(); System.out.println("访问到:" + adjList[pop].data); count++; // 判断该顶点的其他出度对应的顶点是否无入度了,没有的话打印 for (EdgeNode node = adjList[pop].firstEdge; node != null; node = node.next) { if (--adjList[node.getAdjVert()].in == 0) { // 证明该顶点没有了入度,可以压栈 stack.push(node.getAdjVert()); } } } if (count < numVertexes) { throw new Exception("完犊子了,拓扑排序失败"); } } // 边表顶点 class EdgeNode { private int adjVert; private EdgeNode next; private int weight; public EdgeNode(int adjVert) { this.adjVert = adjVert; } public int getAdjVert() { return adjVert; } public void setAdjVert(int adjVert) { this.adjVert = adjVert; } public EdgeNode getNext() { return next; } public void setNext(EdgeNode next) { this.next = next; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } } // 邻接顶点 class VertexNode { private int in;// 入度 private String data; private EdgeNode firstEdge; public VertexNode(int in, String data) { this.in = in; this.data = data; } public int getIn() { return in; } public void setIn(int in) { this.in = in; } public String getData() { return data; } public void setData(String data) { this.data = data; } public EdgeNode getFirstEdge() { return firstEdge; } public void setFirstEdge(EdgeNode firstEdge) { this.firstEdge = firstEdge; } } public static void main(String[] args) { DnGraphTopologic dnGraphTopologic = new DnGraphTopologic(14); dnGraphTopologic.createGraph(); try { dnGraphTopologic.topologicalSort(); } catch (Exception e) { e.printStackTrace(); } } }