拓扑排序,也就是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;
- }
- }
- }