数据结构与算法之图(三)图的拓扑排序

引言

工程中尝尝氛围很多步骤完成,有些步骤可以同步进行,而有些步骤需要某些步骤完成后才能进行,如何安排它们的流程是项目实施要考虑的重要问题,这就是拓扑排序问题。拓扑排序,它可以决定哪些子工程必须要先执行,哪些子工程要在某些工程执行后才可以执行。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。
一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行(对于数据流来说就是死循环)。在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。

拓扑排序思想

1.在有向图中选一个没有前驱的顶点并且输出;
2.从图中删除该顶点和所有以它为尾的弧(白话就是:删除所有和它有关的边);
3.重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。
拓扑排序有点像抽丝剥茧,每一次遍历都会去掉最外层的"壳",抽完为止。
图解
1.针对下图:



有入度0的顶点V1和V6,先把他们放入集合S=[V1,V6],这里随机取出V6遍历,S=[V1]。
2.V6遍历完,去掉和V4、V5的边(入度减1),图结构如下:



3.从集合中取出V1,删除边,V2、V3、V4入度减一后,V4和V3入度为0,S=[V3,V4],图结构如下:

4.从集合中取出V4或者V3(取哪一个看具体算法),这里取V4,V5入度为减一不为0,S为[V3],图结构为:

5,从集合中取出V3,V2、V5入度为0,S=[V2,V5],图结构如下:
image

6.继续输出集合中蒸鱼顶点,最后的该图的一个拓扑排序结果为:

v6—>v1—>v4—>v3—>v5—>v2。
注意:拓扑排序仅反应顶点间的层序关系,输出结果不唯一。

代码实现

代码实现,我们引入一个集合(栈或者队列均可),存放未遍历的入度为0的顶点;引入计数变量检测图是否遍历完。具体实现如下:

package graphic;

import queue.Queue;

/**
 * Created by chenming on 2018/6/16
 * 拓扑排序
 */
public class TopologySort {
    private class VNode {
        int value;//值
        int inDegree;//入度
    }

    private int[][] map;//邻接矩阵
    private VNode[] nodes;

    public TopologySort(int[][] map) {
        this.map = map;
        nodes = new VNode[map.length];
        for (int i = 0; i < map.length; i++) {
            //初始化入度
            VNode node = new VNode();
            node.value = i;
            node.inDegree = getIndegree(i);
            nodes[i] = node;
        }
    }

    /**
     * 从邻接矩阵中得到顶点入度
     *
     * @param index
     * @return
     */
    private int getIndegree(int index) {
        int in = 0;
        for (int i = 0; i < map.length; i++) {
            if (0 < map[i][index] && map[i][index] < Integer.MAX_VALUE) {
                in++;
            }
        }
        return in;
    }

    /**
     * 有向图的拓扑排序,,思路如下:
     * 1,类似广度优先遍历,引入队列保存入度为0的未遍历节点
     * 2.每去掉一个顶点,则去掉该顶点的边,即它所有邻接顶点的顶点入度-1,如果为0则加入队列
     * 3.循环执行步骤2,直到队列为空
     * 4.循环结束检测是否遍历完整,如果没有遍历完全,则表示有回环
     */
    public void topologySort() {
        Queue<Integer> queue = new Queue<>();//保存入度为0的索引
        int numb = map.length;
        int count = 0;//校验用
        //收集入度为0的顶点
        for (int i = 0; i < numb; i++) {
            if (nodes[i].inDegree == 0) {
                queue.enquene(i);
            }
        }

        while (!queue.isEmpty()) {
            int index = queue.dequeue();
            System.out.println("拓扑排序:" + index);
            count++;
            //遍历完"删除"顶点index的所有边,即减少它邻接顶点的入度
            for (int i = 0; i < numb; i++){
                if(map[index][i] > 0 && map[index][i] < Integer.MAX_VALUE){
                    if(--nodes[i].inDegree == 0){
                        //入度减一后,加入队列
                        queue.enquene(i);
                    }

                }
            }

        }

        if(count != numb){
            throw new RuntimeException("图中存在回环");
        }

    }
}

测试图用例:


拓扑排序用例.png

测试代码:

   @Test
    public void testTopol() {
        int INF = Integer.MAX_VALUE;
        int[][] map = {
                {0, INF, INF, INF, 1, 1, INF, INF, INF, INF, INF, 1, INF, INF},//0
                {INF, 0, 1, INF, 1, INF, INF, INF, 1, INF, INF, INF, INF, INF},//1
                {INF, INF, 0, INF, INF, 1, 1, INF, INF, 1, INF, INF, INF, INF},//2
                {INF, INF, 1, 0, INF, INF, INF, INF, INF, INF, INF, INF, INF, 1},//3
                {INF, INF, INF, INF, 0, INF, INF, 1, INF, INF, INF, INF, INF, INF},//4
                {INF, INF, INF, INF, INF, 0, INF, INF, 1, INF, INF, INF, 1, INF},//5
                {INF, INF, INF, INF, INF, 1, 0, INF, INF, INF, INF, INF, INF, INF},//6
                {INF, INF, INF, INF, INF, INF, INF, 0, INF, INF, INF, INF, INF, INF},//7
                {INF, INF, INF, INF, INF, INF, INF, 1, 0, INF, INF, INF, INF, INF},//8
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, 0, 1, 1, INF, INF},//9
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 0, INF, INF, 1},//10
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 0, INF, INF},//11
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, 1, INF, INF, 0, INF},//12
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 0},//13
        };
        TopologySort ts = new TopologySort(map);
        ts.topologySort();
    }

输出:

拓扑排序:0
拓扑排序:1
拓扑排序:3
拓扑排序:4
拓扑排序:2
拓扑排序:6
拓扑排序:5
拓扑排序:8
拓扑排序:12
拓扑排序:7
拓扑排序:9
拓扑排序:10
拓扑排序:11
拓扑排序:13

完整代码地址:数据结构与算法学习JAVA描述GayHub地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,563评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,694评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,672评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,965评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,690评论 6 413
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,019评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,013评论 3 449
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,188评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,718评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,438评论 3 360
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,667评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,149评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,845评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,252评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,590评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,384评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,635评论 2 380

推荐阅读更多精彩内容

  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,891评论 0 13
  • 第一章 绪论 什么是数据结构? 数据结构的定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
    SeanCheney阅读 5,807评论 0 19
  • 数据结构学不好,c++就到后面会很迷,数据结构真滴很重要啊,上机题一定要认真做,紧密的和实际操作的代码联系在一起是...
    Nancy_Shi阅读 734评论 0 4
  • https://zh.visualgo.net/graphds 浅谈图形结构https://zh.visualgo...
    狼之独步阅读 4,186评论 0 0
  • 课程介绍 先修课:概率统计,程序设计实习,集合论与图论 后续课:算法分析与设计,编译原理,操作系统,数据库概论,人...
    ShellyWhen阅读 2,339评论 0 3