当前位置:   article > 正文

轻松学懂图(中)——拓扑排序、Kruskal算法和Prim算法_prim和kruskal和拓扑 王道

prim和kruskal和拓扑 王道

轻松学懂图(中)——拓扑排序、Kruskal算法和Prim算法

概述

​ 上一篇文章将图相关的常见概念大概讲了一遍,今天主要介绍图相关的算法,可能一下子不太能理解,我会尽量用大白话为大家讲解,请耐心将不是很明白的地方多看几遍。如果有不正确的地方,还请指出,一起进步。

​ 下面,我们将开始正文

拓扑排序究竟是啥?
  • AOV网

​ 在了解拓扑排序之前,首先需要知道AOV网(Activity On Vertex Network),它长成下面这样:

在这里插入图片描述

​ 在AVO网中,顶点表示活动(Activity),边用来表示活动之间的先后顺序。不难看出,AOV网是一种有向无环图。

  • AOE网

    与AOV网不同的是每条边都有权值,可以理解为完成某个活动的开销,例如,在上面的AOV网中,如果给每个边加上权值,那么就可以表示花费的时间

  • 拓扑排序

    ​ 在上面那个AOV网中,所有活动之间的关系都通过边表示出来了,如何把这些事情有条不紊的做完对你来说肯定都可以看出来,比如:去菜市场 --> 买菜 --> 买米 --> 做饭 --> 做菜 -->吃饭,这种顺序其实就是拓扑排序,并且也很容易发现的是,对于同一个AOV网拓扑排序的结果可能有多个。

    ​ 但是,倘若这个AOV网有上百个活动,活动之间的先后顺序也错综复杂,此时我们想按照一定的顺序将如此多的事情做完并且也符合活动的先后顺序,此时就不能用肉眼去观察了,那样太费时间了,因此,就出现了拓扑排序算法,那么它是如何实现的呢?让我们先来分析一下我们之前的那个拓扑排序你就明白了,让我们来观察一下下图:

在这里插入图片描述

不知道你通过上图拓扑排序生成的过程是否发现了其中的规律:

  1. 在所有活动中选择一个没有被其他活动所指向的活动(也就是入度为0的点)
  2. 然后再删除掉该点所有的边,即指向其他活动的有向边
  3. 再重复1、2步骤

恭喜你,你已经学会了拓扑排序了!下面就是拓扑排序的部分代码:

	/**
     * 拓扑排序——(可用来判断有向图中是否有环)
     *
     * @return
     */
    @Override
    public List<V> topologicalSort() {
        // 记录遍历过的节点,也即拓扑排序的结果
        List<V> list = new ArrayList<>();
        // 队列
        Queue<Vertex<V, E>> queue = new LinkedList();
        // map映射,记录顶点的入度信息
        Map<Vertex<V, E>, Integer> map = new HashMap<>();

        // 初始化队列和map
        vertices.forEach((v, vertex) -> {
            // 入度
            int in = vertex.inEdges.size();
            // 将一开始入度为0的顶点放入队列,不为0的放入map中
            if (in == 0) {
                queue.offer(vertex);
            } else {
                map.put(vertex, in);
            }
        });
        
        while (!queue.isEmpty()) {
            Vertex<V, E> vertex = queue.poll();
            list.add(vertex.value);
            // 更新它所指向的顶点的入度
            for (Edge<V, E> edge : vertex.outEdges) {
                // 顶点更新后的入度
                int in = map.get(edge.to) - 1;
                if (in == 0) {
                    // 放入队列中
                    queue.offer(edge.to);
                } else {
                    // 更新map中该顶点对应的入度
                    map.put(edge.to, in);
                }
            }
        }
        return list;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

不知道,你有没有注意到我在方法上所写的注释信息:可用来判断有向图中是否有环,这怎么理解呢?

来看看下面的这张图你应该就明白了:
在这里插入图片描述

如果你用之前拓扑排序的方法,就会发现,拓扑排序的结果只有E,也就是说,如果拓扑排序的活动数量小于原先AOV网中活动的数量时,就表明有向图中存在环!

至此,就是拓扑排序的全部内容了!

kruskal(克鲁斯卡尔)算法与prim(普里姆)算法
  • 什么是生成树

    对于连通图(图中任意两点直接都是连通的)来说,如果可以用其中最少的边来表示的连通图,就是这个连通图的最小生成树,下面是示例:

在这里插入图片描述

  • Prim(普里姆)算法

    该算法,用到了集合的思想,首先让 E点 属于集合一,然后其余点属于集合二,然后选择一条连接集合一与集合二的边,并将被选择边所连接的原属于集合二的点加入到集合一中,然后再重复上述步骤选择一条边,直至集合二中没有点为止,这么一听是不是有点蒙,话不多说,直接上图:

在这里插入图片描述

上面为了简单起见,所演示的是无向边且没有权值的,所以每次对边的选择都是随机的,但如果是在有权值的AOE网中,那么久需要每次选择全职最小的即可得到最小生成树了

  • Kruskal(克鲁斯卡尔)算法

    该算法步骤比较简单,即按照权值的大小,每次选择一条权值最小的边即可,并且,会在选择之前进行判断——选择该条边之后是否会形成环,如果会形成环则舍弃,不会形成环就选择,直至边的数量比顶点的数量少1为止。怎么样是不是很简单?来看看步骤是怎么样的:

在这里插入图片描述

说明:

  1. 按照顺序大小选择了权值为2、3、4、5的边并且每次选择的边都不会和之前已经选择的边形成环
  2. 此时最小的边是两条权值为7的,但是不管选择哪一条,都会和已选择的形成环,因此都舍弃
  3. 接下来最权值为8的两条边,发现连接F和C的边不符合,舍去,然后选择连接A和C的边
  4. 此时发现边的数量为5,顶点数量为6,满足 边的数量 = 定点数 - 1,算法结束

至此,你已经学会了拓扑排序、Prim算法和Kruskal算法了!怎么样,是不是觉得图相关的算法那其实并不难。限于时间,这次更新的内容就这么多,之后会更新Dijkstra算法和Bellman-Ford算法。今天的内容如果有错误的地方还望指出,共同进步。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/1010257
推荐阅读
相关标签
  

闽ICP备14008679号