拓补排序是将一个有向无环图中的所有顶点排成一个线性队列,使得如果从顶点a到b存在一条有向边,则顶点a在顶点b之前。一个图可能有若干个不同的拓补顺序序列,如果有向图不含有环,则至少能够找到一个这样的序列。注意,含有环的图是不可能存在拓补顺序的,如果顶点a与b在环上,就既存在从a到b的路径又存在从b到a的路径。不管a与b选择什么顺序,这些路径中的任何一条都将与之相矛盾。拓补排序有什么意义呢?拓补排序在实际生活中和算法中都有很大的应用,比如要排一下几门课程的先后次序,我们可以将课程抽象成结点,将什么课程是什么课程的基础抽象成边,那么该图的一个拓补序列,就是这些课一个可行的先后次序。
拓补排序的思想:1)从图中选取一个入度为0的顶点,并输出之 2)从图中”删除“此顶点,并删除所有以它为目标顶点的边
重复上述两个步骤,直至图为空或找不到入度为0的顶点为止。
我的考虑:从图中删除顶点的话,会导致破坏原图的结构,从而导致某个图往往为了得到它的拓补排序,必须把自身破坏了(删除其顶点及相应的边),这就意味着进行完拓补排序的图就不能进行类似的比如深度优先、广度优先的搜索了。所以我决定不采用删除顶点的办法,改用是否被visited来标识。就是从图中的某个顶点来找它的前驱结点的时候,如果该顶点存在没有被访问过的前驱结点,那么该顶点的入度不为0.这也就意味着如果该顶点所有的前驱结点已经被访问过,那么该顶点的入度为0。
代码实现思路(关于顶点前驱结点,我们可以在顶点类中加一个列表,当用户构造这个图的时候,就把这个顶点相应的前驱结点加入到列表中):
public Stack<T> getTopologicalSort() {
// TODO Auto-generated method stub
Stack<T> vertexStack=new Stack<T>();
for(int i=0;i<vertices.size();i++)
{
VertexInterface<T> nextVertex=getZeroIndegree();
nextVertex.visit();
vertexStack.push(nextVertex.getLabel());
}
return vertexStack;
}
private VertexInterface<T> getZeroIndegree()
{
VertexInterface<T> result=null;
Collection<VertexInterface<T>> mySet=vertices.values();
Iterator<VertexInterface<T>> myIterator=mySet.iterator();
while(myIterator.hasNext())
{
VertexInterface<T> nextVertex=myIterator.next();
if(!nextVertex.isVisited()&&!nextVertex.hasPredecessor())
{
result=nextVertex;
break;
}
}
return result;
}
public boolean hasPredecessor() {
// TODO Auto-generated method stub
boolean result=false;
if(previousVertexList.size()!=0)
{
Iterator<VertexInterface<T>> myIterator=previousVertexList.iterator();
while(myIterator.hasNext())
{
VertexInterface<T> nextVertex=myIterator.next();
if(!nextVertex.isVisited())
{
result=true;
break;
}
}
}
return result;
}