拓扑排序

介绍

1、对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前

2、这样的线性序列称为满足拓扑次序的序列,简称拓扑序列

3、只有有向无环图才存在拓扑序列

4、对于一个有向无环图,可能存在多个拓扑序列

 

概念

1、DGA:有向无环图

2、AOV网

(1)无权有向无环图

(2)顶点表示活动,边表示活动(顶点)发生的先后关系

(3)AOV 网的边不设权值,若存在边 <a,b>,则表示活动 a 必须发生在活动 b 之前

(4)若网中所有活动均可以排出先后顺序(任两个活动之间均确定先后顺序),则称网是拓扑有序的,这个顺序称为网上一个全序

(5)数据在顶点,可以理解为面向对象

3、AOE网

(1)带权有向无环图

(2)边代表活动,顶点代表所有指向它的边所代表的活动均已完成这一事件

(3)由于整个工程只有一个起点和一个终点,网中只有一个入度为 0 的点(源点)和一个出度为 0 的点(汇点)

(4)数据在边上,可以理解为面向过程

(5)“发生”是针对于事件的,也就是图中的顶点。什么叫事件发生了呢?

 

AOE 发生 / 开始 时间

1、发生

(1)针对事件,即图中的顶点

(2)只有在指向该顶点的所有有向边对应的活动结束,该顶点所代表的事件才发生

(3)例:一个事件 C,它仅被两条边 a、b 指向,仅当 a、b 两活动都完成时,事件 C 发生

2、开始

(1)针对活动,即图中的边

(2)只有在一个顶点所代表的事件发生后,从该顶点出发的所有边对应的活动才能开始

(3)既可以在事件一完成就立马开始接下来的活动,也可以推迟活动开始的时间

3、关键路径

(1)整个活动的完成时间是 AOE 图中从始点到终点的最长路径的长度

(2)关键路径上的活动称作关键活动

(3)关键路径不一定只有一条

4、事件最早发生时间 ve = Max{前驱顶点最早开始时间 + 活动权值}

(1)之前所有活动均完成所需的时间,由耗时最长的路径决定

(2)Max{目标顶点的所有前提顶点的最早完成时间 + 对应的前提顶点到目标顶点的活动消耗时间},如:Max{A 最早完成时间 + A 到 C 的活动消耗时间, B 最早完成时间 + B 到 C 的活动消耗时间}

(3)递推公式:从源点开始一直递推到汇点(终点)

ve(源点) = 0
ve(k) = Max{ve{j} + Weight(j, k) }
j 为 k 的任意前驱顶点, Weight(j, k)表示 <j, k> 上的权值

5、事件的最迟发生时间 vl = Min{后继顶点最晚开始时间 - 活动权值} 

(1)在不推迟整个工程完成的前提下,保证后继事件 j 能在其最迟发生时间 vl(j) 能够发生时,该事件最迟必须发生的时间

(2)一个顶点的最迟发生时间由它的后继节点所决定

(3)递推公式:从汇点开始一直递推到源点(起点)

vl(汇点) = ve(汇点)
vl(k) = Min{vl(j) - Weight(k, j)}
k为j的任意前驱

6、活动的最早开始时间 e = 前驱顶点最早开始时间

(1)边的起点时间,为其起点事件的最早发生时间

(2)若边 <k, j> 表示活动 i,则有 e(i) = ve(k)

7、活动的最迟开始时间 l = 后继顶点最晚开始时间 - 活动权值

(1)边所指向的事件(终点)最迟发生时间 - 边对应的活动所需时间

(2)若边 <k, j> 表示活动 i,则有 l(i) = vl(j) - Weight(k, j)

 

逆拓扑排序

1、从有向图中选取一个没有直接的后继节点(即出度为 0)的顶点,并输出

2、从有向图中删去此顶点以及指向它的有向边(入度)

3、重复步骤1、2,产生两种结果

(1)全部顶点均已输出,逆拓扑序列形成,逆拓扑排序完成

(2)或图中还有未输出的顶点,但已跳出处理循环,说明图中还剩下一些顶点,它们都有直接后继节点(即出度不为0),有向无环图中必定存在有向环

4、本质:深度优先

 

拓扑排序

1、从有向图中选取一个没有直接的前驱节点(即入度为 0)的顶点,并输出

2、从有向图中删去此顶点以及它发出的有向边(出度)

3、重复步骤1、2,产生两种结果

(1)全部顶点均已输出,拓扑有序序列形成,拓扑排序完成

(2)或图中还有未输出的顶点,但已跳出处理循环,说明图中还剩下一些顶点,它们都有直接前驱节点(即入度不为0),有向无环图中必定存在有向环

4、本质:广度优先

5、代码实现

import java.util.LinkedList;

public class Digraph {//不带权有向图
    public char[] vertex;//储存顶点
    public int[][] matrix;//邻接矩阵:0代表无边;1代表顶点的出度
    public int[] indegree;//下标对应顶点,储存顶点的入度个数
    public LinkedList<Character> result;//储存拓扑排序后的顶点

    //测试拓扑排序
    public static void main(String[] args) {
        //有向有环图
        char[] vertex1 = {'A', 'B', 'C'};
        int[][] matrix1 = {
                {0, 1, 0},
                {0, 0, 1},
                {1, 0, 0}
        };
        Digraph digraph1 = new Digraph(vertex1, matrix1);
        digraph1.topologySort();
        //有向无环图
        char[] vertex2 = {'A', 'B', 'C', 'D', 'E'};
        int[][] matrix2 = {
                {0, 0, 1, 0, 0},
                {1, 0, 0, 1, 0},
                {0, 0, 0, 0, 1},
                {1, 0, 0, 0, 1},
                {0, 0, 0, 0, 0}
        };
        Digraph digraph2 = new Digraph(vertex2, matrix2);
        digraph2.topologySort();
    }

    public Digraph(char[] vertex, int[][] matrix) {
        this.vertex = vertex;
        this.matrix = matrix;
        this.result = new LinkedList<>();
        this.indegree = new int[vertex.length];
        //统计初始情况时,各顶点入度个数
        for (int i = 0; i < vertex.length; i++) {
            for (int j = 0; j < vertex.length; j++) {
                if (matrix[i][j] == 1) {
                    indegree[j] += 1;
                }
            }
        }
    }

    //拓扑排序
    public void topologySort() {
        //临时储存入度为0的顶点下标
        LinkedList<Integer> temp = new LinkedList<>();
        //初始化temp,储存图的入度为0的顶点下标
        for (int i = 0; i < vertex.length; i++) {
            if (indegree[i] == 0) {
                temp.addLast(i);
            }
        }
        //循环直到没有入度为0的顶点为止
        while (!temp.isEmpty()) {
            //取出入度为0的顶点下标index
            int index = temp.removeFirst();
            //将index顶点放入result末尾
            result.addLast(vertex[index]);
            for (int i = 0; i < vertex.length; i++) {//
                //删除其他顶点(与index顶点有关)的入度
                if (matrix[index][i] == 1) {
                    indegree[i]--;
                    //若该顶点删除(与index顶点有关)入度后,该顶点入度为0,则将其加入temp末尾
                    if (indegree[i] == 0) {
                        temp.addLast(i);
                    }
                }
            }
        }
        if (result.size() == vertex.length) {//所有顶点都能取出
            print();
        } else {//result.size() < vertex.length,说明有环,不能把所有顶点取出
            System.out.println("有向图存在环,无法拓扑排序");
        }
    }

    //输出拓扑排序结果
    public void print() {
        for (Character character : result) {
            System.out.print(character + "\t");
        }
        System.out.println();
    }
}

 

posted @   半条咸鱼  阅读(452)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示