拓扑排序,也就是AOV(activity on vertex),顶点的活动有先后执行顺序,用于有向无环图。
基本思路是
1 找到无入度的点,然后去掉这个点和这个点的出度边。
2 循环执行1直到再也找不到点
3 如果点的个数等于顶点数则生成一个拓扑序列,如果小的话就是有环。
邻接矩阵实现方法:
用列下标循环,如果列没有访问过,则进入行下标循环,如果该列都为0,则表示该列表示的节点没有入度,记下下标。如果该列不是全为0则计数器清0.
用刚存入的下标找到顶点存入拓扑序列,同时将该列下标表示的顶点的出度边都去掉(就是用下标表示的行全部置为0),计数器清0.
重复上面的动作直到再也找不到为止,跳出最外层循环。
邻接表实现方法:
首先定义入度数组,用下标找到位置然后循环邻接表设置入度值。
堆栈初始化,将入度为0的顶点入栈,计数器自增。
当堆栈不为空的时候循环,出栈的顶点(去掉顶点)找到连接的arc链表,链表的保存的序号对应着节点也对应着入度数组的位置(根据序号将入度数组的值自减),如果入度为0则将该顶点入栈(下标找到该顶点),计数器自增。
最后判断个数是否是顶点的个数。
代码:
package nuaa.ds;
public class Digraph {
private CreateGraph g;
private String[] vertexes;//邻接矩阵法所
private int[][] vr; //使用的成员变量
private VertexNode[] vertexNodes;//邻接表所使用的成员变量
/**
* 手动生成有向无环图测试算法
* A->B->E-->F-->H
* ^ ^\ ^
* \ | v /
* C---> D->G
*/
public Digraph(CreateGraph g){
this.g = g;
switch(g){
case AdjacencyMatrix://邻接矩阵
this.vertexes = new String[]{"A","B","C","D","E","F","G","H"};
vr = new int[8][8];
//arcs都有权重
vr[0] = new int[]{0,7,0,0,0,0,0,0};//好
vr[1] = new int[]{0,0,0,0,3,0,0,0};
vr[2] = new int[]{0,0,0,4,0,0,0,0};
vr[3] = new int[]{0,2,0,0,1,0,0,0};//多
vr[4] = new int[]{0,0,0,0,0,5,6,0};
vr[5] = new int[]{0,0,0,0,0,0,0,2};//浪
vr[6] = new int[]{0,0,0,0,0,0,0,4};
vr[7] = new int[]{0,0,0,0,0,0,0,0};//费
break;
case AdjacencyTable:
vertexNodes = new VertexNode[8];
String[] temp = {"A","B","C","D","E","F","G","H"};
for(int i=0;i<vertexNodes.length;i++){
vertexNodes[i] = new VertexNode(temp[i]);
}
vertexNodes[0].arcNode = new ArcNode(1,7);
vertexNodes[1].arcNode = new ArcNode(4,3);
vertexNodes[2].arcNode = new ArcNode(3,4);
vertexNodes[3].arcNode = new ArcNode(1,2);
vertexNodes[3].arcNode.arcNode = new ArcNode(4,1);
vertexNodes[4].arcNode = new ArcNode(5,5);
vertexNodes[4].arcNode.arcNode = new ArcNode(6,6);
vertexNodes[5].arcNode = new ArcNode(7,2);
vertexNodes[6].arcNode = new ArcNode(7,4);
}
}
/**
* AOV(activity on vertex)网,顶点表示活动的网
* 工程上有用,图必须为无回路的图
*/
public void topologicalSort(){//拓扑排序,排序结果不唯一
this.topologicalSort(this.g);
};
private void topologicalSort(CreateGraph g){
switch(g){
case AdjacencyMatrix:
mTopologicalSort();
break;
case AdjacencyTable:
tTopologicalSort();
}
}
//该方法破坏了原来了邻接矩阵,应该拷贝一份再调用更合适
private void mTopologicalSort(){
boolean[] visited = new boolean[vr.length];
int zeroNumber = 0;//判断某列是否全为0
int chosen = 0;//找到序列下一个节点就把下标记录下来
int count = 0;//判断序列个数,得到的图是否是有向无环图
while(true){
//寻找拓扑序列下一节点
for(int i=0;i<vr.length;i++){//i为列下标
if(!visited[i]){//如果该列代表元素没有被塞入拓扑序列
for(int j=0;j<vr.length;j++){//j为行下标,循环判断某列是否为全0
if(vr[j][i]!=0){//有非0元素表示有前序节点
break;//跳出来
}
zeroNumber++;//不然就++自增
}
}
if(zeroNumber==vr.length){//一列都为0
chosen = i;//记录下这个拓扑节点
visited[i] = true;//这个元素要被塞入拓扑序列
break;
}else{
zeroNumber = 0;
}
}
if(zeroNumber==vr.length){//如果找到了拓扑序列下一个节点
visit(vertexes[chosen]);//这里直接打印下。不是很合适
for(int i=0;i<vr.length;i++){
vr[chosen][i] = 0;//该行全部置为0
}
count++;
}else{//找不到下一拓扑节点了
break;
}
zeroNumber = 0;//计数器归0
}
if(count!=vr.length)
System.out.println("--------------有环图---------------");
}
private void tTopologicalSort(){
int[] indegree = this.getIndegree();
Stack<VertexNode> stack = new Stack<VertexNode>();//要拓扑序列用Queue更合适,只是
Stack<VertexNode> saveStack = new Stack<VertexNode>();//为了后面的关键路径算法
int count = 0;//计数器,判断是否有环
for(int i=0;i<indegree.length;i++){
if(indegree[i]==0){
stack.push(vertexNodes[i]);
count++;
}
}
while(!stack.isEmpty()){
VertexNode vertexNode = stack.pop();
saveStack.push(vertexNode);
ArcNode p = vertexNode.arcNode;
while(p!=null){
indegree[p.serialNumber]--;
if(indegree[p.serialNumber]==0){
stack.push(vertexNodes[p.serialNumber]);
count++;
}
p = p.arcNode;
}
}
if(count==vertexNodes.length){
printSeries(saveStack);
} else{
System.out.println("有环图");
}
}
//邻接表求入度,入度in English 怎么说。。。。。。。。。
private int[] getIndegree(){
int[] indegree = new int[this.vertexNodes.length];
for(int i=0;i<vertexNodes.length;i++){
ArcNode p = vertexNodes[i].arcNode;
while(p!=null){
indegree[p.serialNumber]++;//序号表示节点在入度数组的位置
p = p.arcNode;
}
}
return indegree;
}
//递归打印正确的拓扑序列
private void printSeries(Stack<VertexNode> s){
if(s.isEmpty()){
return;
}
VertexNode vertexNode = s.pop();
printSeries(s);
visit(vertexNode);
}
private void visit(String vertex){
System.out.print(vertex+" ");
}
private void visit(VertexNode node){
System.out.print(node.vertex+" ");
}
class VertexNode{
String vertex;
ArcNode arcNode;
public VertexNode(String vertex){
this.vertex = vertex;
}
}
class ArcNode{
int serialNumber;//存放边连着的节点在vertexNode数组里面的位置
int weight;//权重在求最小生成树时用到,拓扑排序没有
ArcNode arcNode;
public ArcNode(int serialNumber,int weight){
this.serialNumber = serialNumber;
this.weight = weight;
}
}
}