拓扑排序算法介绍
拓扑排序解决的是一系列相互依赖的事件的排序问题,比如Ant中有很多的Task,而某些Task依赖于另外的Task,编译之前需要清理空间,打包之前要先编译,但其它一些Task处理顺序可以调换(是无所谓前后,不是并行), 如何安排Task的执行顺序就可以用拓扑排序解决。熟悉Java的朋友应该都知道Spring,一个非常优秀的解决组件(Bean)依赖的框架,组件之间可能有依赖关系,也可能没有关系,如何按顺序创建组件也是相同的问题。本文使用的是图搜索算法里面的深度优先排序算法解决问题。需要特别指出的是拓扑排序算法的结果很可能有多个(不依赖的可以调换位置),而算法也可以有多种,深度优先排序算法只是其中一种而已。拓扑排序为线性排序,效率为O(|V|+|E|),其中|V|表示顶点数,|E|表示边的数量。
拓扑排序算法Java实现
图像(Graph)的Java抽象实现
图可以抽象为顶点和边,分为有向图和无向图,拓扑排序里面使用的事有向图(依赖),本文中图的边用相邻链表方法表示。每一个顶点有名字(name),颜色(color, 搜索时候用来标记处理状态),父节点(parent,搜索结束可以得到多棵树),开始处理时间(discover),结束处理时间(finish)。请注意Vertex类override了equals和hash方法。具体代码如下:
enum Color {
WHITE, GRAY, BLACK
}
static class Vertex {
private String name;
private Color color;
private Vertex parent;
private int discover;
private int finish;
public Vertex(String name) {
this.name = name;
this.color = Color.WHITE;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public Vertex getParent() {
return parent;
}
public void setParent(Vertex parent) {
this.parent = parent;
}
public int getDiscover() {
return discover;
}
public void setDiscover(int discover) {
this.discover = discover;
}
public int getFinish() {
return finish;
}
public void setFinish(int finish) {
this.finish = finish;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Vertex other = (Vertex) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
static class Graph {
private Set<Vertex> vertexSet = new HashSet<Vertex>();
// 相邻的节点
private Map<Vertex, Vertex[]> adjacencys = new HashMap<Vertex, Vertex[]>();
public Set<Vertex> getVertexSet() {
return vertexSet;
}
public void setVertexSet(Set<Vertex> vertexSet) {
this.vertexSet = vertexSet;
}
public Map<Vertex, Vertex[]> getAdjacencys() {
return adjacencys;
}
public void setAdjacencys(Map<Vertex, Vertex[]> adjacencys) {
this.adjacencys = adjacencys;
}
}
深度优先算法
遍历图的顶点,如果当前顶点还没有处理过(color为white),就处理该节点(visitVertex),处理节点的算法(visitVertex)也不难,时间维度加1,当前节点颜色置为gray(开始处理),然后优先处理其子节点(深度优先),结束之后时间维度加1,当前节点颜色置为black(结束处理)。此时就可以把该节点放到目标链表里面了(最终排好序的链表)。由于Java里面Integer值不可变(Immutable),只能选择全局变量或者自己实现时间计数器,本文选择后者(TimeRecorder)。代码如下:
static class TimeRecorder {
private int time = 0;
public int getTime() {
return time;
}
public void setTime(int time) {
this.time = time;
}
}
public static Vertex[] topologicalSort(Graph graph) {
Set<Vertex> vertexSet = graph.getVertexSet();
if (vertexSet.size() < 2) {
return vertexSet.toArray(new Vertex[0]);
}
LinkedList<Vertex> sortedList = new LinkedList<Vertex>();
TimeRecorder recorder = new TimeRecorder();
for (Vertex vertex : vertexSet) {
if (vertex.color == Color.WHITE) {
visitVertex(graph, vertex, recorder, sortedList);
}
}
return sortedList.toArray(new Vertex[0]);
}
/**
* 深度优先搜索(Depth First Search)
*/
public static void visitVertex(Graph graph, Vertex vertex,
TimeRecorder recorder, LinkedList<Vertex> sortedList) {
recorder.time += 1;
vertex.color = Color.GRAY;
vertex.discover = recorder.time;
Map<Vertex, Vertex[]> edgeMap = graph.getAdjacencys();
Vertex[] adjacencys = edgeMap.get(vertex);
if (adjacencys != null && adjacencys.length > 0) {
for (Vertex adjacency : adjacencys) {
if (adjacency.color == Color.WHITE) {
adjacency.parent = vertex;
visitVertex(graph, adjacency, recorder, sortedList);
}
}
}
recorder.time += 1;
vertex.color = Color.BLACK;
vertex.finish = recorder.time;
sortedList.addLast(vertex);
}
构建图以及测试
我们的测试图例如下(箭头的方向表示的是依赖):
为了打印排好序的结果,实现了printVertex函数。测试代码如下:
public static void main(String[] args) {
Graph graph = new Graph();
Set<Vertex> vertexSet = graph.getVertexSet();
Map<Vertex, Vertex[]> edgeMap = graph.getAdjacencys();
Vertex twoVertex = new Vertex("2");
Vertex threeVertex = new Vertex("3");
Vertex fiveVertex = new Vertex("5");
Vertex sevenVertex = new Vertex("7");
Vertex eightVertex = new Vertex("8");
Vertex nineVertex = new Vertex("9");
Vertex tenVertex = new Vertex("10");
Vertex elevenVertex = new Vertex("11");
vertexSet.add(twoVertex);
vertexSet.add(threeVertex);
vertexSet.add(fiveVertex);
vertexSet.add(sevenVertex);
vertexSet.add(eightVertex);
vertexSet.add(nineVertex);
vertexSet.add(tenVertex);
vertexSet.add(elevenVertex);
edgeMap.put(twoVertex, new Vertex[] { elevenVertex });
edgeMap.put(nineVertex, new Vertex[] { elevenVertex, eightVertex });
edgeMap.put(tenVertex, new Vertex[] { elevenVertex, threeVertex });
edgeMap.put(elevenVertex, new Vertex[] { sevenVertex, fiveVertex });
edgeMap.put(eightVertex, new Vertex[] { sevenVertex, threeVertex });
Vertex[] sortedVertexs = topologicalSort(graph);
printVertex(sortedVertexs);
}
public static void printVertex(Vertex[] Vertexs) {
for (Vertex vertex : Vertexs) {
System.out.println(vertex.getName() + " discover time:"
+ vertex.getDiscover() + " finish time:"
+ vertex.getFinish());
}
}
后记
以上Java实现参考的是算法导论的深度优先排序算法。如果想对排序的精确度有更好的控制,可以在Vertex类中加一个priority属性。每一次遍历之前都针对顶点以priority即可。参考链接:维基百科