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

来源:互联网 发布:淘宝卖家怎么设置限购 编辑:程序博客网 时间:2024/04/29 12:35

目录

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

0 0
原创粉丝点击