方法一:
(1)在有向图中选一个没有前驱(入度为0)的点输出。
(2)从图中删除该顶点和所有以它为尾的弧。
重复以上步骤,直至全部顶点均已输出,或者当前图中不存在五前驱的顶点为止。
在实现中,我们可以用一个队列存入所有入度为0的顶点。然后依次删除这些顶点,和其对应的边,如果对应边删除后其终点的入度减为0者也将其存入队列中,如此循环下去,直到队列为空。最后比较列表中的节点数是否等于图的顶点数,如果不等者图存在一个环。
基于以上的规则可以用下面的代码来实现图的拓扑排序。
- /**
- * 图的拓扑排序2,该方法会改变图的结构
- * @param <T>
- * @return
- */
- public static<T> List<T> topologicalSort2(Graph<T> graph){
- Queue<T> vQueue = new LinkedList<T>();
- List<T> vList = new ArrayList<T>();
- Set<T> vSet = graph.vertexSet();
- Set<T> neighbors = null;
- T vertex = null;
- //将入度为0的节点存入队列中
- for(T v : vSet){
- if(graph.getIndegree(v) == 0){
- vQueue.add(v);
- vList.add(v);
- }
- }
- while(!vQueue.isEmpty()){
- vertex = vQueue.poll();
- neighbors = graph.getNeighbors(vertex);
- for(T neighbor : neighbors){
- graph.removeEdge(vertex, neighbor);//删除该边
- if(graph.getIndegree(neighbor) == 0){//若neighbor的入度变为0,者也将其加入队列中
- vQueue.add(neighbor);
- vList.add(neighbor);
- }
- }
- }
- if(vList.size() != graph.numberOfVertices()){
- throw new IllegalStateException("存在环");
- }
- return vList;
- }
方法二:
可以证明在被路径p(v,w)连接的任意顶点所在的图中,对于v和w来说,v在列表中必须出现在w之前。
依据这个结论可以对图的每个节点进行一个dfs的遍历,最终的节点列表就是拓扑排序的一个结果(要对这个列表反转).
- /**
- * 图的拓扑排序
- * @param <T>
- * @param graph
- * @return
- */
- public static<T> List<T> topologicalSort(Graph<T> graph){
- List<T> vList = new ArrayList<T>();
- graph.allUnVisted();
- for(T v : graph.vertexSet()){
- if(graph.getState(v) == VertexState.UNVISITED){
- try{
- dfsHandler(graph, v, true, vList);
- }catch (IllegalStateException e) {
- throw new IllegalStateException("图有一个环");
- }
- }
- }
- Collections.reverse(vList);
- return vList;
- }
测试:
针对下面这个图进行测试
/**
- * 测试图的拓扑排序
- */
- public void testTopologicalSort(){
- List<String> vList = GraphUtil.topologicalSort(graph);
- System.out.println(Arrays.toString(vList.toArray()));
- vList = GraphUtil.topologicalSort2(graph);
- System.out.println(Arrays.toString(vList.toArray()));
- }
Output:
[c2, c5, c1, c3, c7, c6, c4, c8]
[c1, c2, c3, c5, c4, c6, c7, c8]