一,问题描述

给定一个有向图G=(V,E),将之进行拓扑排序,如果图有环,则提示异常。

要想实现图的算法,如拓扑排序、最短路径……并运行看输出结果,首先就得构造一个图。由于构造图的方式有很多种,这里假设图的数据存储在一个文件中,

每一行包含如下的信息:
LinkID,SourceID,DestinationID,Cost
其中,LinkID为该有向边的索引,SourceID为该有向边的起始顶点的索引,DestinationID为该有向边的终止顶点的索引,Cost为该有向边的权重。

0,0,1,1
1,0,2,2
2,0,3,1
3,2,1,3
4,3,1,1
5,2,3,1
6,3,2,1

(以上示例引用自网上,该图仅用来表示存储图信息的文件内容的格式,对拓扑排序而言,上图显然存在环)

对于以下的拓扑排序程序,只用到了SourceID,和DestionatinID这两个字段。拓扑序列以顶点的索引表示。后续会实现无向图的最短路径算法,就会用到Cost这个字段啦!!!

二,算法实现思路

拓扑排序,其实就是寻找一个入度为0的顶点,该顶点是拓扑排序中的第一个顶点序列,将之标记删除,然后将与该顶点相邻接的顶点的入度减1,再继续寻找入度为0的顶点,直至所有的顶点都已经标记删除或者图中有环。

从上可以看出,关键是寻找入度为0的顶点。

一种方式是遍历整个图中的顶点,找出入度为0的顶点,然后标记删除该顶点,更新相关顶点的入度,由于图中有V个顶点,每次找出入度为0的顶点后会更新相关顶点的入度,因此下一次又要重新扫描图中所有的顶点。故时间复杂度为O(V^2)

由于删除入度为0的顶点时,只会更新与它邻接的顶点的入度,即只会影响与之邻接的顶点。但是上面的方式却遍历了图中所有的顶点的入度。

改进的另一种方式是:先将入度为0的顶点放在栈或者队列中。当队列不空时,删除一个顶点v,然后更新与顶点v邻接的顶点的入度。只要有一个顶点的入度降为0,则将之入队列。此时,拓扑排序就是顶点出队的顺序。该算法的时间复杂度为O(V+E)

三,拓扑排序方法的实现

该算法借助队列来实现时,感觉与 二叉树的 层序遍历算法很相似啊。说明这里面有广度优先的思想。

第一步:遍历图中所有的顶点,将入度为0的顶点 入队列。

第二步:从队列中出一个顶点,打印顶点,更新该顶点的邻接点的入度(减1),如果邻接点的入度减1之后变成了0,则将该邻接点入队列。

第三步:一直执行上面 第二步,直到队列为空。

 1     public void topoSort() throws Exception{
 2         int count = 0;//判断是否所有的顶点都出队了,若有顶点未入队(组成环的顶点),则这些顶点肯定不会出队
 3
 4         Queue<Vertex> queue = new LinkedList<>();// 拓扑排序中用到的栈,也可用队列.
 5         //扫描所有的顶点,将入度为0的顶点入队列
 6         Collection<Vertex> vertexs = directedGraph.values();
 7         for (Vertex vertex : vertexs)
 8             if(vertex.inDegree == 0)
 9                 queue.offer(vertex);
10         //度为0的顶点出队列并且更新它的邻接点的入度
11         while(!queue.isEmpty()){
12             Vertex v = queue.poll();
13             System.out.print(v.vertexLabel + " ");//输出拓扑排序的顺序
14             count++;
15             for (Edge e : v.adjEdges)
16                 if(--e.endVertex.inDegree == 0)
17                     queue.offer(e.endVertex);
18         }
19         if(count != directedGraph.size())
20             throw new Exception("Graph has circle");
21     }

第7行for循环:先将图中所有入度为0的顶点入队列。

第11行while循环:将入度为0的顶点出队列,并更新与之邻接的顶点的入度,若邻接顶点的入度降为0,则入队列(第16行if语句)。

第19行if语句判断图中是否有环。因为,只有在每个顶点出队时,count++。对于组成环的顶点,是不可能入队列的,因为组成环的顶点的入度不可能为0(第16行if语句不会成立).

因此,如果有环,count的值 一定小于图中顶点的个数。

四,完整代码实现

DirectedGraph.java中定义了图 数据结构,(图的实现可参考:数据结构--图 的JAVA实现(上))。并根据FileUtil.java中得到的字符串构造图。

构造 图之后,topoSort方法实现了拓扑排序。

 1 import java.util.Collection;
 2 import java.util.LinkedHashMap;
 3 import java.util.LinkedList;
 4 import java.util.List;
 5 import java.util.Map;
 6 import java.util.Queue;
 7
 8 /*
 9  * 用来实现拓扑排序的有向无环图
10  */
11 public class DirectedGraph {
12
13     private class Vertex{
14         private String vertexLabel;// 顶点标识
15         private List<Edge> adjEdges;
16         private int inDegree;// 该顶点的入度
17
18         public Vertex(String verTtexLabel) {
19             this.vertexLabel = verTtexLabel;
20             inDegree = 0;
21             adjEdges = new LinkedList<Edge>();
22         }
23     }
24
25     private class Edge {
26         private Vertex endVertex;
27
28         // private double weight;
29         public Edge(Vertex endVertex) {
30             this.endVertex = endVertex;
31         }
32     }
33
34     private Map<String, Vertex> directedGraph;
35
36     public DirectedGraph(String graphContent) {
37         directedGraph = new LinkedHashMap<String, DirectedGraph.Vertex>();
38         buildGraph(graphContent);
39     }
40
41     private void buildGraph(String graphContent) {
42         String[] lines = graphContent.split("\n");
43         Vertex startNode, endNode;
44         String startNodeLabel, endNodeLabel;
45         Edge e;
46         for (int i = 0; i < lines.length; i++) {
47             String[] nodesInfo = lines[i].split(",");
48             startNodeLabel = nodesInfo[1];
49             endNodeLabel = nodesInfo[2];
50             startNode = directedGraph.get(startNodeLabel);
51             if(startNode == null){
52                 startNode = new Vertex(startNodeLabel);
53                 directedGraph.put(startNodeLabel, startNode);
54             }
55             endNode = directedGraph.get(endNodeLabel);
56             if(endNode == null){
57                 endNode = new Vertex(endNodeLabel);
58                 directedGraph.put(endNodeLabel, endNode);
59             }
60
61             e = new Edge(endNode);//每读入一行代表一条边
62             startNode.adjEdges.add(e);//每读入一行数据,起始顶点添加一条边
63             endNode.inDegree++;//每读入一行数据,终止顶点入度加1
64         }
65     }
66
67     public void topoSort() throws Exception{
68         int count = 0;
69
70         Queue<Vertex> queue = new LinkedList<>();// 拓扑排序中用到的栈,也可用队列.
71         //扫描所有的顶点,将入度为0的顶点入队列
72         Collection<Vertex> vertexs = directedGraph.values();
73         for (Vertex vertex : vertexs)
74             if(vertex.inDegree == 0)
75                 queue.offer(vertex);
76
77         while(!queue.isEmpty()){
78             Vertex v = queue.poll();
79             System.out.print(v.vertexLabel + " ");
80             count++;
81             for (Edge e : v.adjEdges)
82                 if(--e.endVertex.inDegree == 0)
83                     queue.offer(e.endVertex);
84         }
85         if(count != directedGraph.size())
86             throw new Exception("Graph has circle");
87     }
88 }

FileUtil.java负责从文件中读取图的信息。将文件内容转换成 第一点 中描述的字符串格式。--该类来源于网络

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;public final class FileUtil
{/** * 读取文件并按行输出* @param filePath* @param spec 允许解析的最大行数, spec==null时,解析所有行* @return* @author* @since 2016-3-1*/public static String read(final String filePath, final Integer spec){File file = new File(filePath);// 当文件不存在或者不可读时if ((!isFileExists(file)) || (!file.canRead())){System.out.println("file [" + filePath + "] is not exist or cannot read!!!");return null;}BufferedReader br = null;FileReader fb = null;StringBuffer sb = new StringBuffer();try{fb = new FileReader(file);br = new BufferedReader(fb);String str = null;int index = 0;while (((spec == null) || index++ < spec) && (str = br.readLine()) != null){sb.append(str + "\n");
//                System.out.println(str);
}}catch (IOException e){e.printStackTrace();}finally{closeQuietly(br);closeQuietly(fb);}return sb.toString();}/** * 写文件* @param filePath 输出文件路径* @param content 要写入的内容* @param append 是否追加* @return* @author s00274007* @since 2016-3-1*/public static int write(final String filePath, final String content, final boolean append){File file = new File(filePath);if (content == null){System.out.println("file [" + filePath + "] invalid!!!");return 0;}// 当文件存在但不可写时if (isFileExists(file) && (!file.canRead())){return 0;}FileWriter fw = null;BufferedWriter bw = null;try{if (!isFileExists(file)){file.createNewFile();}fw = new FileWriter(file, append);bw = new BufferedWriter(fw);bw.write(content);}catch (IOException e){e.printStackTrace();return 0;}finally{closeQuietly(bw);closeQuietly(fw);}return 1;}private static void closeQuietly(Closeable closeable){try{if (closeable != null){closeable.close();}}catch (IOException e){}}private static boolean isFileExists(final File file){if (file.exists() && file.isFile()){return true;}return false;}
}

测试类:TestTopoSort.java

 1 public class TestTopoSort {
 2     public static void main(String[] args) {
 3         String graphFilePath;
 4         if(args.length == 0)
 5             graphFilePath = "F:\\xxx";
 6         else
 7             graphFilePath = args[0];
 8
 9         String graphContent = FileUtil.read(graphFilePath, null);//从文件中读取图的数据
10         DirectedGraph directedGraph = new DirectedGraph(graphContent);
11         try{
12             directedGraph.topoSort();
13         }catch(Exception e){
14             System.out.println("graph has circle");
15             e.printStackTrace();
16         }
17     }
18 }

有向图的拓扑排序算法JAVA实现。相关推荐

  1. 有向图的拓扑排序算法JAVA实现

    一,问题描述 给定一个有向图G=(V,E),将之进行拓扑排序,如果图有环,则提示异常. 要想实现图的算法,如拓扑排序.最短路径--并运行看输出结果,首先就得构造一个图.由于构造图的方式有很多种,这里假 ...

  2. 在linux下实现拓扑排序,数据结构——有向图(拓扑排序算法)

    package zieckey.datastructure.study.graph; /** * 有方向图 * * @author zieckey */ public class DirectedGr ...

  3. Java实现有向图的拓扑排序

    1.拓扑排序 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G ...

  4. 有向图的拓扑排序的理解和简单实现(Java)

    如果图中存在环(回路),那么该图不存在拓扑排序,在这里我们讨论的都是无环的有向图. 什么是拓扑排序 一个例子 对于一部电影的制作过程,我们可以看成是一个项目工程.所有的工程都可以分为若干个" ...

  5. 拓扑排序之java实现_有向图和拓扑排序Java实现

    package practice; import java.util.ArrayDeque; import java.util.Iterator; import java.util.Stack; pu ...

  6. 【zz】如何去理解 拓扑排序算法

    from http://www.cnblogs.com/shanyou/archive/2006/11/16/562861.html 查看Castle的代码,在Castle.Core中内部的数据结构采 ...

  7. vant coupon 时间戳如何计算_计软考研双日练 | 如何计算拓扑排序算法的时间复杂度?...

    ☝☝☝ 软件工程考研独家平台 撰稿 | 康康哥 编辑 | 丽丽姐 本文由懂计算机.软件工程的博士师哥原创 双日练:NO.20200610 若将n个顶点e条弧的有向图采用邻接表存储,则拓扑排序算法的时间 ...

  8. 数据结构之图:有向图的拓扑排序,Python代码实现——26

    有向图的拓扑排序 拓扑排序介绍 什么是拓扑排序? 一个有向图的拓扑排序(Topological sort 或 Topological ordering)是根据其有向边从顶点U到顶点V对其所有顶点的一个 ...

  9. AOV网中的拓扑排序(Java实现)

    拓扑排序 问题引入 在实际生活中,有很多的活动安排是存在先后顺序的关系的,要先做完一件事才能做后面一件事,或者说通过这样安排达到的效果会更好,就像大学当中的课程安排,举个栗子,计算机专业的同学一般是先 ...

  10. JavaScript实现topologicalSort拓扑排序算法(附完整源码)

    JavaScript实现topologicalSort拓扑排序算法(附完整源码) Comparator.js完整源代码 LinkedListNode.js完整源代码 LinkedList.js完整源代 ...

最新文章

  1. huffman树和huffman编码
  2. Objective-c 网络编程1 Web请求和响应
  3. RSA遭骇 Token 换?不换?
  4. FPGA开发要懂得使用硬件分析仪调试——ILA
  5. EC20模块、主机休眠唤醒机制
  6. Bootstrap4+MySQL前后端综合实训-Day10-AM【实训汇报-下午返校、项目代码(7个包+7个Html页面)】
  7. 车主无忧:为什么放弃开源Kafka?
  8. .NET Core Community 首个千星项目诞生:CAP
  9. consul通过web接口的kv存储操作
  10. prettytensor 的使用
  11. PowerDesigner之PDM(物理概念模型)
  12. multipartfile获取数据_详解SpringMVC使用MultipartFile实现文件的上传
  13. linux 每日学一点《linux性能测试初步概况》
  14. 【名词解释】7.UML类图
  15. SQL Server 2016如何创建数据库
  16. Unity发布PC版本接入支付(威富通)
  17. 考试/shuai 学生的宿命啊
  18. 毕业论文完成,感谢帮助过我的人
  19. ❤️React Hooks⭐
  20. 通过WebView实现简单的浏览器

热门文章

  1. DLP3010EVM-LC and DLP2010EVM-LC常见问题答疑
  2. 2015/7/28(总结昨天抄底操作失败-割肉自保)
  3. c语言从入门到弃坑,从入门到放弃?《全面战争:三国》是怎么让我半小时内弃坑的...
  4. 全新型App开放框架—Clouda
  5. Python分析《三国演义》中的社交网络
  6. 入侵大型网站的完整思路
  7. java中实现注册时Email邮件激活验证
  8. vue实现横向时间轴组件
  9. 这位日本网友和谷歌街景的故事,感动了58万人
  10. 数据分析在网络营销中的意义有哪些