目录

1 问题描述

2 解决方案

2.1 基于减治法实现

2.2 基于深度优先查找实现


1 问题描述

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

那么,何为拓扑排序?

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


2 解决方案

2.1 基于减治法实现

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

具体代码如下:

package com.liuzhen.chapterFour;

import java.util.Stack;

public class TopologicalSorting {
//方法1:基于减治法:寻找图中入度为0的顶点作为即将遍历的顶点,遍历完后,将此顶点从图中删除
/*
* 参数adjMatrix:给出图的邻接矩阵值
* 参数source:给出图的每个顶点的入度值
* 该函数功能:返回给出图的拓扑排序序列
*/
public char[] getSourceSort(int[][] adjMatrix,int[] source){
int len = source.length; //给出图的顶点个数
char[] result = new char[len]; //定义最终返回路径字符数组
int count = 0; //用于计算当前遍历的顶点个数
boolean judge = true;
while(judge){
for(int i = 0;i < source.length;i++){
if(source[i] == 0){ //当第i个顶点入度为0时,遍历该顶点
result[count++] = (char) ('a'+i);
source[i] = -1; //代表第i个顶点已被遍历
for(int j = 0;j < adjMatrix[0].length;j++){ //寻找第i个顶点的出度顶点
if(adjMatrix[i][j] == 1)
source[j] -= 1; //第j个顶点的入度减1
}
}
}
if(count == len)
judge = false;
}
return result;
}
/*
* 参数adjMatrix:给出图的邻接矩阵值
* 函数功能:返回给出图每个顶点的入度值
*/
public int[] getSource(int[][] adjMatrix){
int len = adjMatrix[0].length;
int[] source = new int[len];
for(int i = 0;i < len;i++){
//若邻接矩阵中第i列含有m个1,则在该列的节点就包含m个入度,即source[i] = m
int count = 0;
for(int j = 0;j < len;j++){
if(adjMatrix[j][i] == 1)
count++;
}
source[i] = count;
}
return source;
} public static void main(String[] args){
TopologicalSorting test = new TopologicalSorting();
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}};
int[] source = test.getSource(adjMatrix);
System.out.println("给出图的所有节点(按照字母顺序排列)的入度值:");
for(int i = 0;i < source.length;i++)
System.out.print(source[i]+"\t");
System.out.println();
char[] result = test.getSourceSort(adjMatrix, source); System.out.println("给出图的拓扑排序结果:");
for(int i = 0;i < result.length;i++)
System.out.print(result[i]+"\t");
}
}

 运行结果:

给出图的所有节点(按照字母顺序排列)的入度值:
0 0 2 1 2
给出图的拓扑排序结果:
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
for each node n in S do
    visit(n) 
function visit(node n)
    if n has not been visited yet then
        mark n as visited
        for each node m with an edgefrom m to ndo
            visit(m)
        add n to L

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

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

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

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

具体代码如下:

package com.liuzhen.chapterFour;

import java.util.Stack;

public class TopologicalSorting {

    //方法2:基于深度优先查找发(DFS)获取拓扑排序
public int count1 = 0;
public Stack<Character> result1;
/*
* adjMatrix是待遍历图的邻接矩阵
* value是待遍历图顶点用于是否被遍历的判断依据,0代表未遍历,非0代表已被遍历
*/
public void dfs(int[][] adjMatrix,int[] value){
result1 = new Stack<Character>();
for(int i = 0;i < value.length;i++){
if(value[i] == 0)
dfsVisit(adjMatrix,value,i);
}
}
/*
* adjMatrix是待遍历图的邻接矩阵
* value是待遍历图顶点用于是否被遍历的判断依据,0代表未遍历,非0代表已被遍历
* number是当前正在遍历的顶点在邻接矩阵中的数组下标编号
*/
public void dfsVisit(int[][] adjMatrix,int[] value,int number){
value[number] = ++count1; //把++count1赋值给当前正在遍历顶点判断值数组元素,变为非0,代表已被遍历
for(int i = 0;i < value.length;i++){
if(adjMatrix[number][i] == 1 && value[i] == 0) //当,当前顶点的相邻有相邻顶点可行走且其为被遍历
dfsVisit(adjMatrix,value,i); //执行递归,行走第i个顶点
}
char temp = (char) ('a' + number);
result1.push(temp);
} public static void main(String[] args){
TopologicalSorting test = new TopologicalSorting();
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}}; int[] value = new int[5];
test.dfs(adjMatrix, value);
System.out.println();
System.out.println("使用DFS方法得到拓扑排序序列的逆序:");
System.out.println(test.result1);
System.out.println("使用DFS方法得到拓扑排序序列:");
while(!test.result1.empty())
System.out.print(test.result1.pop()+"\t"); }
}

运行结果:

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

参考资料:

1.拓扑排序的原理及其实现

算法笔记_023:拓扑排序(Java)的更多相关文章

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

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

  2. 算法笔记_018:旅行商问题(Java)

    目录 1 问题描述 2 解决方案 2.1 蛮力法 2.2 减治法 2.2.1 Johson-Trotter算法 2.2.2 基于字典序的算法   1 问题描述 何为旅行商问题?按照非专业的说法,这个问 ...

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

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

  4. 算法练习5---快速排序Java版

    基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成 ...

  5. 【Python排序搜索基本算法】之拓扑排序

    拓扑排序是对有向无环图的一种排序,满足例如以下两个条件: 1.每一个顶点出现且仅仅出现一次. 2.若A在序列中排在B的前面.则在图中不存在从B到A的路径. 如上的无环有向图,v表示顶点:v=['a', ...

  6. 算法笔记_014:合并排序(Java)

    1 问题描述 给定一组数据,使用合并排序得到这组数据的非降序排列. 2 解决方案 2.1 合并排序原理简介 引用自百度百科: 合并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Div ...

  7. 算法笔记_129:计数排序(Java)

    目录 1 问题描述 2 解决方案 2.1比较计数排序 2.2 分布计数排序   1 问题描述 给定一组数据,请使用计数排序,得到这组数据从小到大的排序序列. 2 解决方案 2.1比较计数排序 下面算法 ...

  8. 算法笔记_036:预排序(Java)

    目录 1 问题描述 2 解决方案 2.1 检验数组中元素的唯一性 2.2 模式计算   1 问题描述 在计算机科学中,预排序是一种很古老的思想.实际上,对于排序算法的兴趣很大程度上是因为这样一个事实: ...

  9. 算法练习4---冒泡排序java版

    冒泡排序的基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒.即:每当两相邻的数比较后发现它们的排序与排序要求相反 ...

随机推荐

  1. 【计算几何】【二分图判定】Gym - 101485C - Cleaning Pipes

    题意:有n个水井,每个水井发出一些管线(都是线段),然后每条管线上最多只有一个水井.所有从不同的水井发出的管线的相交点都是清洁点(不存在清洁点是大于两条管线点的交点).你需要在某些管线上放出一些机器人 ...

  2. poj 2342 && hdu 1520 树形dp

    题意:有n个人,接下来n行是n个人的价值,再接下来n行给出l,k说的是l的上司是k,这里注意l与k是不能同时出现的 链接:点我 dp[i][1] += dp[j][0], dp[i][0] += ma ...

  3. YII 关联查询

    通过外键自己关联自己

  4. 动软代码生成器连接Oracle 11g

      首先要说明的是:如果你连接的是远程的Oracle服务器,你本地机器必须装Oracle客户端,然后 用sqldeveloper 先建立一个连接. 然后你才能用.NET动软代码生成器连接到数据库. 因 ...

  5. JS 中对变量类型的判断

    总结:1. 一般简单的使用 typeof 或 instanceof 检测(这两种检测的不完全准确)          2. 完全准确的使用 原生js中的 Object.prototype.toStri ...

  6. CentOS下的yum upgrade和yum update区别,没事别乱用,和Ubuntu的update不一样!

    说明:生产环境对软件版本和内核版本要求非常精确,别没事有事随便的进行yum update操作!!!!!!!!! yum update:升级所有包同时也升级软件和系统内核 yum upgrade:只升级 ...

  7. Use a TL431 shunt regulator to limit high ac input voltage

    Most isolated, offline SMPSs (switched-mode power supplies), including flyback, forward, and resonan ...

  8. GNU诞生三十周年

    1983年9月27日,MIT人工智能实验室的Richard Stallman在新闻组宣布了雄 心勃勃的GNU(Gnu's Not Unix)操作系统计划,他计划创造一个Unix兼容的自由软件系统,包含 ...

  9. ice地址

    http://www.zeroc.com/download/eclipse

  10. 如何编译Linux内核

    内核,是一个操作系统的核心.它负责管理系统的进程.内存.设备驱动程序.文件和网络系统,决定着系统的性能和稳定性.Linux作为一个自由软件,在广 大爱好者的支持下,内核版本不断更新.新的内核修订了旧内 ...