拓扑排序算法介绍
拓扑排序解决的是一系列相互依赖的事件的排序问题,比如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方法。具体代码如下:
- enumColor{
- WHITE,GRAY,BLACK
- }
- staticclassVertex{
- privateStringname;
- privateColorcolor;
- privateVertexparent;
- privateintdiscover;
- privateintfinish;
- publicVertex(Stringname){
- this.name=name;
- this.color=Color.WHITE;
- }
- publicStringgetName(){
- returnname;
- }
- publicvoidsetName(Stringname){
- this.name=name;
- }
- publicColorgetColor(){
- returncolor;
- }
- publicvoidsetColor(Colorcolor){
- this.color=color;
- }
- publicVertexgetParent(){
- returnparent;
- }
- publicvoidsetParent(Vertexparent){
- this.parent=parent;
- }
- publicintgetDiscover(){
- returndiscover;
- }
- publicvoidsetDiscover(intdiscover){
- this.discover=discover;
- }
- publicintgetFinish(){
- returnfinish;
- }
- publicvoidsetFinish(intfinish){
- this.finish=finish;
- }
- @Override
- publicinthashCode(){
- finalintprime=31;
- intresult=1;
- result=prime*result+((name==null)?0:name.hashCode());
- returnresult;
- }
- @Override
- publicbooleanequals(Objectobj){
- if(this==obj)
- returntrue;
- if(obj==null)
- returnfalse;
- if(getClass()!=obj.getClass())
- returnfalse;
- Vertexother=(Vertex)obj;
- if(name==null){
- if(other.name!=null)
- returnfalse;
- }elseif(!name.equals(other.name))
- returnfalse;
- returntrue;
- }
- }
- staticclassGraph{
- privateSet<Vertex>vertexSet=newHashSet<Vertex>();
- //相邻的节点
- privateMap<Vertex,Vertex[]>adjacencys=newHashMap<Vertex,Vertex[]>();
- publicSet<Vertex>getVertexSet(){
- returnvertexSet;
- }
- publicvoidsetVertexSet(Set<Vertex>vertexSet){
- this.vertexSet=vertexSet;
- }
- publicMap<Vertex,Vertex[]>getAdjacencys(){
- returnadjacencys;
- }
- publicvoidsetAdjacencys(Map<Vertex,Vertex[]>adjacencys){
- this.adjacencys=adjacencys;
- }
- }
深度优先算法
遍历图的顶点,如果当前顶点还没有处理过(color为white),就处理该节点(visitVertex),处理节点的算法(visitVertex)也不难,时间维度加1,当前节点颜色置为gray(开始处理),然后优先处理其子节点(深度优先),结束之后时间维度加1,当前节点颜色置为black(结束处理)。此时就可以把该节点放到目标链表里面了(最终排好序的链表)。由于Java里面Integer值不可变(Immutable),只能选择全局变量或者自己实现时间计数器,本文选择后者(TimeRecorder)。代码如下:
- staticclassTimeRecorder{
- privateinttime=0;
- publicintgetTime(){
- returntime;
- }
- publicvoidsetTime(inttime){
- this.time=time;
- }
- }
- publicstaticVertex[]topologicalSort(Graphgraph){
- Set<Vertex>vertexSet=graph.getVertexSet();
- if(vertexSet.size()<2){
- returnvertexSet.toArray(newVertex[0]);
- }
- LinkedList<Vertex>sortedList=newLinkedList<Vertex>();
- TimeRecorderrecorder=newTimeRecorder();
- for(Vertexvertex:vertexSet){
- if(vertex.color==Color.WHITE){
- visitVertex(graph,vertex,recorder,sortedList);
- }
- }
- returnsortedList.toArray(newVertex[0]);
- }
- /**
- *深度优先搜索(DepthFirstSearch)
- */
- publicstaticvoidvisitVertex(Graphgraph,Vertexvertex,
- TimeRecorderrecorder,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(Vertexadjacency: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函数。测试代码如下:
- publicstaticvoidmain(String[]args){
- Graphgraph=newGraph();
- Set<Vertex>vertexSet=graph.getVertexSet();
- Map<Vertex,Vertex[]>edgeMap=graph.getAdjacencys();
- VertextwoVertex=newVertex("2");
- VertexthreeVertex=newVertex("3");
- VertexfiveVertex=newVertex("5");
- VertexsevenVertex=newVertex("7");
- VertexeightVertex=newVertex("8");
- VertexnineVertex=newVertex("9");
- VertextenVertex=newVertex("10");
- VertexelevenVertex=newVertex("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,newVertex[]{elevenVertex});
- edgeMap.put(nineVertex,newVertex[]{elevenVertex,eightVertex});
- edgeMap.put(tenVertex,newVertex[]{elevenVertex,threeVertex});
- edgeMap.put(elevenVertex,newVertex[]{sevenVertex,fiveVertex});
- edgeMap.put(eightVertex,newVertex[]{sevenVertex,threeVertex});
- Vertex[]sortedVertexs=topologicalSort(graph);
- printVertex(sortedVertexs);
- }
- publicstaticvoidprintVertex(Vertex[]Vertexs){
- for(Vertexvertex:Vertexs){
- System.out.println(vertex.getName()+"discovertime:"
- +vertex.getDiscover()+"finishtime:"
- +vertex.getFinish());
- }
- }
后记
以上Java实现参考的是算法导论的深度优先排序算法。如果想对排序的精确度有更好的控制,可以在Vertex类中加一个priority属性。每一次遍历之前都针对顶点以priority即可。参考链接:维基百科
拓扑排序算法介绍
拓扑排序解决的是一系列相互依赖的事件的排序问题,比如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方法。具体代码如下:
- enumColor{
- WHITE,GRAY,BLACK
- }
- staticclassVertex{
- privateStringname;
- privateColorcolor;
- privateVertexparent;
- privateintdiscover;
- privateintfinish;
- publicVertex(Stringname){
- this.name=name;
- this.color=Color.WHITE;
- }
- publicStringgetName(){
- returnname;
- }
- publicvoidsetName(Stringname){
- this.name=name;
- }
- publicColorgetColor(){
- returncolor;
- }
- publicvoidsetColor(Colorcolor){
- this.color=color;
- }
- publicVertexgetParent(){
- returnparent;
- }
- publicvoidsetParent(Vertexparent){
- this.parent=parent;
- }
- publicintgetDiscover(){
- returndiscover;
- }
- publicvoidsetDiscover(intdiscover){
- this.discover=discover;
- }
- publicintgetFinish(){
- returnfinish;
- }
- publicvoidsetFinish(intfinish){
- this.finish=finish;
- }
- @Override
- publicinthashCode(){
- finalintprime=31;
- intresult=1;
- result=prime*result+((name==null)?0:name.hashCode());
- returnresult;
- }
- @Override
- publicbooleanequals(Objectobj){
- if(this==obj)
- returntrue;
- if(obj==null)
- returnfalse;
- if(getClass()!=obj.getClass())
- returnfalse;
- Vertexother=(Vertex)obj;
- if(name==null){
- if(other.name!=null)
- returnfalse;
- }elseif(!name.equals(other.name))
- returnfalse;
- returntrue;
- }
- }
- staticclassGraph{
- privateSet<Vertex>vertexSet=newHashSet<Vertex>();
- //相邻的节点
- privateMap<Vertex,Vertex[]>adjacencys=newHashMap<Vertex,Vertex[]>();
- publicSet<Vertex>getVertexSet(){
- returnvertexSet;
- }
- publicvoidsetVertexSet(Set<Vertex>vertexSet){
- this.vertexSet=vertexSet;
- }
- publicMap<Vertex,Vertex[]>getAdjacencys(){
- returnadjacencys;
- }
- publicvoidsetAdjacencys(Map<Vertex,Vertex[]>adjacencys){
- this.adjacencys=adjacencys;
- }
- }
深度优先算法
遍历图的顶点,如果当前顶点还没有处理过(color为white),就处理该节点(visitVertex),处理节点的算法(visitVertex)也不难,时间维度加1,当前节点颜色置为gray(开始处理),然后优先处理其子节点(深度优先),结束之后时间维度加1,当前节点颜色置为black(结束处理)。此时就可以把该节点放到目标链表里面了(最终排好序的链表)。由于Java里面Integer值不可变(Immutable),只能选择全局变量或者自己实现时间计数器,本文选择后者(TimeRecorder)。代码如下:
- staticclassTimeRecorder{
- privateinttime=0;
- publicintgetTime(){
- returntime;
- }
- publicvoidsetTime(inttime){
- this.time=time;
- }
- }
- publicstaticVertex[]topologicalSort(Graphgraph){
- Set<Vertex>vertexSet=graph.getVertexSet();
- if(vertexSet.size()<2){
- returnvertexSet.toArray(newVertex[0]);
- }
- LinkedList<Vertex>sortedList=newLinkedList<Vertex>();
- TimeRecorderrecorder=newTimeRecorder();
- for(Vertexvertex:vertexSet){
- if(vertex.color==Color.WHITE){
- visitVertex(graph,vertex,recorder,sortedList);
- }
- }
- returnsortedList.toArray(newVertex[0]);
- }
- /**
- *深度优先搜索(DepthFirstSearch)
- */
- publicstaticvoidvisitVertex(Graphgraph,Vertexvertex,
- TimeRecorderrecorder,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(Vertexadjacency: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函数。测试代码如下:
- publicstaticvoidmain(String[]args){
- Graphgraph=newGraph();
- Set<Vertex>vertexSet=graph.getVertexSet();
- Map<Vertex,Vertex[]>edgeMap=graph.getAdjacencys();
- VertextwoVertex=newVertex("2");
- VertexthreeVertex=newVertex("3");
- VertexfiveVertex=newVertex("5");
- VertexsevenVertex=newVertex("7");
- VertexeightVertex=newVertex("8");
- VertexnineVertex=newVertex("9");
- VertextenVertex=newVertex("10");
- VertexelevenVertex=newVertex("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,newVertex[]{elevenVertex});
- edgeMap.put(nineVertex,newVertex[]{elevenVertex,eightVertex});
- edgeMap.put(tenVertex,newVertex[]{elevenVertex,threeVertex});
- edgeMap.put(elevenVertex,newVertex[]{sevenVertex,fiveVertex});
- edgeMap.put(eightVertex,newVertex[]{sevenVertex,threeVertex});
- Vertex[]sortedVertexs=topologicalSort(graph);
- printVertex(sortedVertexs);
- }
- publicstaticvoidprintVertex(Vertex[]Vertexs){
- for(Vertexvertex:Vertexs){
- System.out.println(vertex.getName()+"discovertime:"
- +vertex.getDiscover()+"finishtime:"
- +vertex.getFinish());
- }
- }
后记
以上Java实现参考的是算法导论的深度优先排序算法。如果想对排序的精确度有更好的控制,可以在Vertex类中加一个priority属性。每一次遍历之前都针对顶点以priority即可。参考链接:维基百科