算法笔记_023:拓扑排序(Java)

目录

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.拓扑排序的原理及其实现

时间: 2024-10-27 13:13:35

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

数据结构和算法17 之拓扑排序

本文为博主原创文章,转载请注明出处:http://blog.csdn.net/eson_15/article/details/51194219 这一节我们学习一个新的排序算法,准确的来说,应该叫"有向图的拓扑排序".所谓有向图,就是A->B,但是B不能到A.与无向图的区别是,它的边在邻接矩阵里只有一项(友情提示:如果对图这种数据结构部不太了解的话,可以先看一下这篇博文:数据结构和算法之 无向图.因为拓扑排序是基于图这种数据结构的). 有向图的邻接矩阵如下表所示: A B C A

算法总结之拓扑排序

拓扑排序 1.一般应用       拓扑排序常用来确定一个依赖关系集中,事物发生的顺序.例如,在日常工作中,可能会将项目拆分成A.B.C.D四个子部分来完成,但A依赖于B和D,C依赖于D.为了计算这个项目进行的顺序,可对这个关系集进行拓扑排序,得出一个线性的序列,则排在前面的任务就是需要先完成的任务. 2.实现的基本方法 (1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它. (2)从网中删去该顶点,并且删去从该顶点发出的全部有向边. (3)重复上述两步,直到剩余的网中不再存在没有前趋

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

目录 1 问题描述 2 解决方案 2.1 蛮力法   1 问题描述 何为旅行商问题?按照非专业的说法,这个问题要求找出一条n个给定的城市间的最短路径,使我们在回到触发的城市之前,对每个城市都只访问一次.这样该问题就可以表述为求一个图的最短哈密顿回路的问题.(哈密顿回路:定义为一个对图的每个顶点都只穿越一次的回路) 很容易看出来,哈密顿回路也可以定义为n+1个相邻顶点v1,v2,v3,...,vn,v1的一个序列.其中,序列的第一个顶点和最后一个顶点是相同的,而其它n-1个顶点都是互不相同的.并且

数据结构课程笔记_拓扑排序

何谓拓扑排序? 由某个集合上的一个偏序得到该集合上的一个全序,这个操作叫做拓扑排序. 如何得到一个有向图的拓扑排序? 按照有向图给出的次序关系,将图中顶点排成一个线性序列,对于有向图中没有限定次序关系的顶点,则可以人为加上任意的次序关系,由此所得顶点的线性序列称之为拓扑有序序列. 如何进行拓扑排序? 1.从有向图中选取一个没有前驱的顶点: 2.从有向图中删去此顶点以及所有以它为尾的弧: 重复上述两步直至图空,或者图中找不到无前驱的顶点为止,后一种情况说明图中有环. 算法中需要用定量描述代替定性概

拓扑排序 java

import java.util.*; /** * 拓扑排序:(1)在有向图中选一个没有前驱的顶点且输出之 (2)从图中删除该顶点和所有以它为尾的弧 * 重复以上两步骤,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止.后者说明有向图中有环. * */ public class TopologicalTest { private static final int V_COUNT = 12; public static void main(String[] args) { //初始化节点

有向图和拓扑排序Java实现

package practice; import java.util.ArrayDeque; import java.util.Iterator; import java.util.Stack; public class TestMain { public static void main(String[] args) { Digraph a = new Digraph(13); a.addEdge(0, 1);a.addEdge(0, 5);/*a.addEdge(2, 3);*/a.addE

hdu1285 拓扑排序 java实现

/* 解题思路: 这是一个典型的拓扑排序,这里我们需要从输入的时候获取没个点的入度,若入度为零的, 我们可以从小到大依次输出来.每当输出一个点时,需要把它所广联的边全部消除 (即把该点所相连的点的入度减一),重复上面操作. */ import java.util.Scanner; public class Main { //定义全局变量,方面后面使用 static int n,m; //比赛队伍数和输入比赛结果次数 static int[] degree,sorted; //每个点的入度数和是否

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

基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列. 例如 3 1 5 2 7 9 3 0 首先以3为基准数,基准数的意思就是以这个数为参考,其他数和它做比较,现在例如有两个人,分别从左边和右边开始找,右边的人要找到比基准数3小的数,左边的人找比基准数3大的数,找到以后进行交换,右边的人先开始找,例如上面的数组,右边的人从0开始找,0比3小

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

目录 1 问题描述 2 解决方案 2.1比较计数排序 2.2 分布计数排序   1 问题描述 给定一组数据,请使用计数排序,得到这组数据从小到大的排序序列. 2 解决方案 2.1比较计数排序 下面算法的时间复杂度为O(n^2),空间复杂度为O(n).此方法对于任意一组数据均可排序. 具体代码如下: package com.liuzhen.practice; public class Main { public void comparisonCountingSort(int[] A) { int[