1 问题描述

给定一个有向图,求取此图的拓扑排序序列。

那么,何为拓扑排序?

定义:将有向图中的顶点以线性方式进行排序。即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点v的前面。

2 解决方案

2.1 基于减治法实现


实现原理:不断地做这样一件事,在余下的有向图中求取一个源(source)(PS:定义入度为0的顶点为有向图的源),它是一个没有输入边的顶点,然后把它和所有从它出发的边都删除。(如果有多个这样的源,可以任意选择一个。如果这样的源不存在,算法停止,此时该问题无解),下面给出《算法设计与分析基础》第三版上一个配图:

  1. package com.liuzhen.chapterFour;
  2. import java.util.Stack;
  3. public class TopologicalSorting {
  4. //方法1:基于减治法:寻找图中入度为0的顶点作为即将遍历的顶点,遍历完后,将此顶点从图中删除
  5. /*
  6. * 参数adjMatrix:给出图的邻接矩阵值
  7. * 参数source:给出图的每个顶点的入度值
  8. * 该函数功能:返回给出图的拓扑排序序列
  9. */
  10. public char[] getSourceSort(int[][] adjMatrix,int[] source){
  11. int len = source.length; //给出图的顶点个数
  12. char[] result = new char[len]; //定义最终返回路径字符数组
  13. int count = 0; //用于计算当前遍历的顶点个数
  14. boolean judge = true;
  15. while(judge){
  16. for(int i = 0;i < source.length;i++){
  17. if(source[i] == 0){ //当第i个顶点入度为0时,遍历该顶点
  18. result[count++] = (char) ('a'+i);
  19. source[i] = -1; //代表第i个顶点已被遍历
  20. for(int j = 0;j < adjMatrix[0].length;j++){ //寻找第i个顶点的出度顶点
  21. if(adjMatrix[i][j] == 1)
  22. source[j] -= 1; //第j个顶点的入度减1
  23. }
  24. }
  25. }
  26. if(count == len)
  27. judge = false;
  28. }
  29. return result;
  30. }
  31. /*
  32. * 参数adjMatrix:给出图的邻接矩阵值
  33. * 函数功能:返回给出图每个顶点的入度值
  34. */
  35. public int[] getSource(int[][] adjMatrix){
  36. int len = adjMatrix[0].length;
  37. int[] source = new int[len];
  38. for(int i = 0;i < len;i++){
  39. //若邻接矩阵中第i列含有m个1,则在该列的节点就包含m个入度,即source[i] = m
  40. int count = 0;
  41. for(int j = 0;j < len;j++){
  42. if(adjMatrix[j][i] == 1)
  43. count++;
  44. }
  45. source[i] = count;
  46. }
  47. return source;
  48. }
  49. public static void main(String[] args){
  50. TopologicalSorting test = new TopologicalSorting();
  51. int[][] adjMatrix = {{0,0,1,0,0},{0,0,1,0,0},{0,0,0,1,1},{0,0,0,0,1},{0,0,0,0,0}};
  52. int[] source = test.getSource(adjMatrix);
  53. System.out.println("给出图的所有节点(按照字母顺序排列)的入度值:");
  54. for(int i = 0;i < source.length;i++)
  55. System.out.print(source[i]+"\t");
  56. System.out.println();
  57. char[] result = test.getSourceSort(adjMatrix, source);
  58. System.out.println("给出图的拓扑排序结果:");
  59. for(int i = 0;i < result.length;i++)
  60. System.out.print(result[i]+"\t");
  61. }
  62. }

运行结果:

  1. 给出图的所有节点(按照字母顺序排列)的入度值:
  2. 0 0 2 1 2
  3. 给出图的拓扑排序结果:
  4. a b c d e

2.2 基于深度优先查找实现

引用自网友博客中一段解释:

除了使用上面2.1中所示算法之外,还能够借助深度优先遍历来实现拓扑排序。这个时候需要使用到栈结构来记录拓扑排序的结果。

同样摘录一段维基百科上的伪码:

L ← Empty list that will contain the sorted nodes

S ← Set of all nodes with no outgoing edges

  1. for each node n in S do
  2. visit(n)
  3. function visit(node n)
  4. if n has not been visited yet then
  5. mark n as visited
  6. for each node m with an edgefrom m to ndo
  7. visit(m)
  8. add n to L

DFS的实现更加简单直观,使用递归实现。利用DFS实现拓扑排序,实际上只需要添加一行代码,即上面伪码中的最后一行:add n to L。

需要注意的是,将顶点添加到结果List中的时机是在visit方法即将退出之时。

此处重点在于理解:上面伪码中的最后一行:add n to L,对于这一行的理解重点在于对于递归算法执行顺序的理解,递归执行顺序的核心包括两点:1.先执行递归,后进行回溯;2.遵循栈的特性,先进后出。此处可以参考本人另外一篇博客:递归执行顺序的探讨

下面请看一个出自《算法设计与分析基础》第三版上一个配图:

  1. package com.liuzhen.chapterFour;
  2. import java.util.Stack;
  3. public class TopologicalSorting {
  4. //方法2:基于深度优先查找发(DFS)获取拓扑排序
  5. public int count1 = 0;
  6. public Stack<Character> result1;
  7. /*
  8. * adjMatrix是待遍历图的邻接矩阵
  9. * value是待遍历图顶点用于是否被遍历的判断依据,0代表未遍历,非0代表已被遍历
  10. */
  11. public void dfs(int[][] adjMatrix,int[] value){
  12. result1 = new Stack<Character>();
  13. for(int i = 0;i < value.length;i++){
  14. if(value[i] == 0)
  15. dfsVisit(adjMatrix,value,i);
  16. }
  17. }
  18. /*
  19. * adjMatrix是待遍历图的邻接矩阵
  20. * value是待遍历图顶点用于是否被遍历的判断依据,0代表未遍历,非0代表已被遍历
  21. * number是当前正在遍历的顶点在邻接矩阵中的数组下标编号
  22. */
  23. public void dfsVisit(int[][] adjMatrix,int[] value,int number){
  24. value[number] = ++count1; //把++count1赋值给当前正在遍历顶点判断值数组元素,变为非0,代表已被遍历
  25. for(int i = 0;i < value.length;i++){
  26. if(adjMatrix[number][i] == 1 && value[i] == 0) //当,当前顶点的相邻有相邻顶点可行走且其为被遍历
  27. dfsVisit(adjMatrix,value,i); //执行递归,行走第i个顶点
  28. }
  29. char temp = (char) ('a' + number);
  30. result1.push(temp);
  31. }
  32. public static void main(String[] args){
  33. TopologicalSorting test = new TopologicalSorting();
  34. int[][] adjMatrix = {{0,0,1,0,0},{0,0,1,0,0},{0,0,0,1,1},{0,0,0,0,1},{0,0,0,0,0}};
  35. int[] value = new int[5];
  36. test.dfs(adjMatrix, value);
  37. System.out.println();
  38. System.out.println("使用DFS方法得到拓扑排序序列的逆序:");
  39. System.out.println(test.result1);
  40. System.out.println("使用DFS方法得到拓扑排序序列:");
  41. while(!test.result1.empty())
  42. System.out.print(test.result1.pop()+"\t");
  43. }
  44. }

运行结果:

  1. 使用DFS方法得到拓扑排序序列的逆序:
  2. [e, d, c, a, b]
  3. 使用DFS方法得到拓扑排序序列:
  4. b a c d e

Java实现拓扑排序的更多相关文章

  1. 拓扑排序(三)之 Java详解

    前面分别介绍了拓扑排序的C和C++实现,本文通过Java实现拓扑排序. 目录 1. 拓扑排序介绍 2. 拓扑排序的算法图解 3. 拓扑排序的代码说明 4. 拓扑排序的完整源码和测试程序 转载请注明出处 ...

  2. 无前趋的顶点优先的拓扑排序方法(JAVA)(转载http://128kj.iteye.com/blog/1706968)

    无前趋的顶点优先的拓扑排序方法 该方法的每一步总是输出当前无前趋(即人度为零)的顶点,其抽象算法可描述为:     NonPreFirstTopSort(G){//优先输出无前趋的顶点       w ...

  3. Java排序算法——拓扑排序

    package graph; import java.util.LinkedList; import java.util.Queue; import thinkinjava.net.mindview. ...

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

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

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

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

  6. 拓扑排序获取所有可能序列JAVA实现

    在看算法基础这本书,看到有向无环图,其中介绍到了拓扑排序,讲到了获取拓扑序列的方法,结合自己的理解,用JAVA代码实现了获取所有可能序列,水平有限,效率什么的就没有考虑,下面贴上代码: package ...

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

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

  8. JAVA邻接矩阵实现拓扑排序

    由于一直不适用邻接表 ,现在先贴一段使用邻接矩阵实现图的拓扑排序以及判断有无回路的问题.自己做的图.将就看吧. package TopSort; import java.util.LinkedList ...

  9. 算法笔记_145:拓扑排序的应用(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 给出一些球,从1~N编号,他们的重量都不相同,也用1~N标记加以区分(这里真心恶毒啊,估计很多WA都是因为这里),然后给出一些约束条件,< a ...

随机推荐

  1. [hdu1079]简单博弈

    题意:两个人玩游戏,给定一个日期,他们轮流选择日期,可以选择当前日期的下一天,如果下一个月也有这一天的话则也可以选择下一个月的这一天.超过某一日期的人输. 思路:以天为状态,则一共有300多万个左右的 ...

  2. gather函数

    gather(input, dim, index):根据  index,在  dim  维度上选取数据,输出的  size  与  index  一致 # input (Tensor) – 源张量 # ...

  3. Java代码生成器多表配置优化,增加自定义实体功能

    目录 前言 多表配置优化 自定义实体 杂谈 结语 前言   最近利用零碎的时间对代码生成器做了进一步更新:优化多表配置模块,增加自定义实体功能,美化单表和多表配置的UI界面,修复用户反馈的若干bug, ...

  4. vscode+eslint自动格式化vue代码的方法

    前言 使用vscode开发vue项目的时候,为了编码格式的统一化,使用eslint规范进行格式化.此时通过eslint插件可以实现对vue代码的自动格式化. 使用方式 在vscode的插件模块处,搜索 ...

  5. JS理论-:一只tom猫告诉你构造函数 实例 实例原型 实例原型的实例原型是什么

    参考地址:https://github.com/mqyqingfeng/Blog/issues/2 感谢这位大佬 下面说说我的理解: 第一,看下人物: tom--一只叫tom的猫 Cat()--猫的构 ...

  6. 前端:参数传错了,spring-boot:那错误信息我给你显示的友好点儿

    之前两篇文章 Spring-boot自定义参数校验注解和如何在spring-boot中进行参数校验,我们介绍了,参数校验以及如何自定义参数校验注解,但是当传递参数出错时,只是把错误信息打印到了控制台, ...

  7. shrine

    0x01 import flask import os app = flask.Flask(__name__) app.config['FLAG'] = os.environ.pop('FLAG') ...

  8. java ->Servlet接口

    JavaWeb核心之Servlet Servlet简介 什么是Servlet(控制器的作用) Servlet 运行在服务端的Java小程序,是sun公司提供一套规范(接口),用来处理客户端请求.响应给 ...

  9. GitHub 热点速览 Vol.20:VSCode 插件全家桶新增画图小能手

    作者:HelloGitHub-小鱼干 摘要:后浪,这个五月热词用来概括 GitHub 本周热点无疑是最佳词汇.Deno 这个 Node.js 作者制造出来的后浪,掀起了 GitHub Trending ...

  10. Spark_Streaming整合Kafka

    Spark Streaming 整合 Kafka ​ 一.版本说明二.项目依赖三.整合Kafka        3.1 ConsumerRecord        3.2 生产者属性        3 ...