有向图的拓补排序算法

拓扑排序算法介绍

拓扑排序解决的是一系列相互依赖的事件的排序问题,比如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();
		}
	}
}

 

  

 

posted @ 2018-04-14 02:49  Booker808  阅读(320)  评论(0编辑  收藏  举报