本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是算法和数据结构专题20篇文章,我们继续最小生成树算法,来把它说完。

在上一篇文章当中,我们主要学习了最小生成树的Kruskal算法。今天我们来学习一下Prim算法,来从另一个角度来理解一下这个问题。

从边到点

我们简单回顾一下Kruskal算法的原理,虽然上篇文章当中用了很多篇幅,但是原理非常简单。本质上就是我们对图中所有的边按照长度进行排序,之后我们按照顺序依次把它作为树的骨干,加入到树上来。

在此过程当中,我们为了避免导致产生环,而破坏树结构,所以使用了并查集算法来作为维护。只会考虑那些不在一个连通块中的边,否则就会构成环路。

很多人在学习了这个算法之后,会将它理解成贪心问题,或者是并查集的一个使用场景。这么理解倒也没错,但是在这个问题当中,还有更好的解释。

这个解释就是边集的扩张

整个Kruskal运行的过程是我们不断选择边加入树中的过程,对于n个点的图来说,我们需要n-1条边。如果我们专注于这个被选出来边的集合,那么算法开始的时候,它是空集,运行结束之后,它含有n-1条边,达到饱和。

当然你也可以换个角度来看,如果我们的关注点在点上,那么最小生成树构建的过程也同样可以看成是点集的拓张。只不过点和边不同,边可以选择,但是点不可以,点只能通过选择边来覆盖。比如我们看下下图:

在图中,我们左边是一棵已经构成的树,当我们连通AE之后,我们就用边覆盖了E点。点是依托于边的,不通过边,是无法覆盖点的。

我们从空集开始,除了第一条边可以覆盖两个点之外,之后的每一条边都连通一个已经覆盖的点和一个没有覆盖的点。那么,同样也是通过n-1条边可以覆盖n个点。这个就是Prim算法的核心思路,也就是点集的拓张。

整体思路

我们明白了Prim的核心思想是点集的拓张之后就容易了,由于我们每次选择的边两边一定是一个已经覆盖的点和没有覆盖的点。所以我们生成的树是一条边一条边逐渐长大的,而不是像Kruskal那样东拼西凑起来的。

我们来看个例子:

我们已经连通了ABCD四个点,其中CE的长度是7,DF的长度是9,EF的长度是5。虽然EF的长度小于CE,但是由于我们必须要连通一个已经覆盖的点和没有覆盖的点,虽然EF的距离更小,我们也不能选择。只能选择CE,所以在整个算法运行的过程中间,这棵树是逐渐变大的。如果是Kruskal,我们肯定会先连通EF,再连通CE,整个算法运行的过程当中,各个部分都是隔开的,最后的树其实是逐渐“拼凑”出来的。

和Kruskal维护集合相比,我们维护点有没有覆盖过则要容易得多。因为树已经选择的边是不会修改的,所以我们只需要用一个数组标记一下每个位置的点有没有覆盖即可。简单的bool类型就可以实现,非常方便。

所以我们的问题只剩下了一个,如何保证我们生成出来的树的路径和最小呢?

关于这个问题的回答Prim和Kruskal一样,就是贪心。我们每次选择最小的边进行拓展,Kruskal是对所有边进行排序,然后依次判断能否选择。那么Prim算法怎么用贪心呢?

其实也很简单,我们也很容易想明白。Prim算法对边有限制,只能选择已经覆盖的点和没有覆盖的点之间的连边。我们给这些边起个名字,叫做可增广边。那么,显然我们要做的就是在可增广边当中选择一条最短的进行增广。

问题就只剩下了一个,我们怎么选择和维护这个最短的可增广边呢,难道每次拓充之后,都进行排序吗?

显然不是,因为每次都排序带来的开销太大了,我们可以用一个数据结构来维护这些边,让它们按照边的长度进行排序。这个数据结构我们应该很熟悉了,就是我们已经遇见过好几次的——优先队列

我们排序的键也已经很明显了,就是边的长度,边是否合法的判断也很简单,我们只要判断一下是否存在没有覆盖的点即可。于是整个流程就串起来了,我们可以先来把流程理一下,写出它的流程:

选择一个点u,当做已经覆盖
把u所有相连的边加入队列
循环循环 从队列头部弹出边如果边合法弹出跳出循环获取边的两个端点将未覆盖的端点所有边加入队列
直到所有点都已经覆盖

最后,我们看下Python的实现,首先是优先队列的部分,这个逻辑我们可以利用现成的heapq来实现。

import 

然后是Prim算法的实现,这里为了存储方便,我们使用了邻接表来存储边的信息。邻接表其实是一个链表的数组,数组里的每一个元素都是一个链表的头结点。这个链表存储的是某一个节点的所有边信息。

比如邻接表中下标1的链表存储的就是与1这个节点相连的所有边的信息。这个数据结构在我们存储树和图的时候经常用到,不过也并不复杂,我们也不用真的实现一个链表,因为可以通过数组来模拟。

edges 

结尾

到这里,关于Prim算法的介绍就结束了。其实本质上来说Prim和Kruskal是最小生成树算法的一体两面,两者的本质都是一样的,就是增广。只不过不同的是,两者一个是点的增广一个是边的增广而已。但是由于点的增广也依托于边,所以Prim当中既用到点来判断是否覆盖,又用到边的信息来增广点。

如果单纯从算法逻辑入手,没有能够理解它的本质,不仅很容易把这两个算法搞混淆,也容易在写代码的时候搞晕,不知道到底要维护什么,要拓展什么。

增广的思想在图论相关的算法当中经常用到(比如网络流),并不只是在最小生成树当中出现,因此理解这一概念对于我们后续的学习非常重要。希望大家都能领会其中的精髓。

各位看官大人,求赏个关注吧~

prim算法_最小生成树的本质是什么?Prim算法道破天机相关推荐

  1. prim算法_最小生成树(Kruskal和Prim算法)

    文章和资源同步更新至微信公众号:算法工程师之路 8月份会开启每日算法题系列,值得期待哦 上一篇文章,我们讲了图的创建和遍历,其中遍历的算法主要有BFS(广度优先算法)和DFS(深度优先算法)两种,并且 ...

  2. 最小生成树 算法_最小生成树算法

    最小生成树 算法 距离我的资格考试只有十天的路程,我决定离开教科书,改回写作. 毕竟,如果我可以解释这些概念,那么我应该能够通过对它们的测试,对吗? 好吧,今天我很有趣地介绍了算法课程中的一个概念:最 ...

  3. prim算法_自动生成随机迷宫(1)prim算法

          "程序 = 数据  + 算法",一款好的作品不单单是代码的堆砌,还有其灵魂的部分,那就是算法:算法是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机 ...

  4. 蝴蝶优化算法_腾讯机智团队分享--AllReduce算法的前世今生

    从事分布式深度学习相关工作的同学,应该都频繁地用到了AllReduce(规约)操作. 图1 AllReduce的示意图 但是对于训练框架中集成的AllReduce相关操作,其背后实现的原理是什么? 除 ...

  5. python魔方程序算法_魔方机器人(一)还原算法

    魔方机器人(一)还原算法 还原算法简介 我使用的是Thislethwaite还原算法.其简介如下:一般的魔方还原的解法是通过逐块(piece or block or layer)还原来减少下一步剩余块 ...

  6. 机器学习算法_明确解释:4种机器学习算法

    您是涉足机器学习的数据科学家吗? 如果是,那么您应该阅读此内容. 定义,目的,流行算法和用例-全部说明 > Photo by Andy Kelly on Unsplash 机器学习已经从科幻小说 ...

  7. 病虫害模型算法_基于深度学习的目标检测算法综述

    sigai 基于深度学习的目标检测算法综述 导言 目标检测的任务是找出图像中所有感兴趣的目标(物体),确定它们的位置和大小,是机器视觉领域的核心问题之一.由于各类物体有不同的外观,形状,姿态,加上成像 ...

  8. k means聚类算法_一文读懂K-means聚类算法

    1.引言 什么是聚类?我们通常说,机器学习任务可以分为两类,一类是监督学习,一类是无监督学习.监督学习:训练集有明确标签,监督学习就是寻找问题(又称输入.特征.自变量)与标签(又称输出.目标.因变量) ...

  9. etc的常见算法_谈常用的几个机器学习算法,学懂算法也可以这么简单!

    本文的目的,是务实.简洁地盘点一番当前机器学习算法.文中内容结合了个人在查阅资料过程中收集到的前人总结,同时添加了部分自身总结,在这里,依据实际使用中的经验,将对此类模型优缺点及选择详加讨论 主要回顾 ...

最新文章

  1. ASPNET开源项目
  2. glutSwapBuffers函数用法
  3. 浏览器工作原理(四):浏览器事件解读
  4. Kafka 详细配置参数说明
  5. Linux——SUID、SGID、SBIT简介
  6. “苹果压根不关心开发人员的利益”!
  7. 该不该怼老板,关键在于你会不会
  8. Wireshark 常用过滤
  9. 怎样设置才能允许外网访问MySQL
  10. win10电脑怎么将html网页做成壁纸,手把手教你win10动态桌面怎么设置
  11. word文档中删除空行(段落空行与缩进空行)
  12. 用户名修改后进入不了计算机,更改计算机用户名后不能登录到桌面怎么办?
  13. 利用Lightroom添加边框及批量导出
  14. C#中的动态类型(Dynamic)
  15. 钢笔工具使用目前的最高水平
  16. 辉芒微IO单片机FT60F011A-RB
  17. python高德 查询县_Python和高德开放平台——地名地址空间化及采集POI信息
  18. 软件设计师-设计模式
  19. 数学建模学习笔记(清风)——插值算法
  20. 杭州程序员从互联网跳央企,晒一天工作和收入,网友:待一年就废

热门文章

  1. .vue文件中style标签的几个标识符
  2. python调用tcpdump抓包过滤
  3. 自定义控件详解(三):Canvas效果变换
  4. 界面Hello world
  5. RegisterStartupScript 和 RegisterClientScriptBlock 的区别
  6. Python GUI编程-了解相关技术[整理]
  7. java静态变量和实例变量的区别6_java静态变量的与实例变量的区别
  8. uni 根目录路径_如何解决uniapp图片路径错误问题
  9. android xml ui编辑器,Android Studio(八):使用Layout Editor设计UI
  10. nessus安装后进不去了_Centos7安装图形化界面