图的最小生成树算法--普里姆(Prim)算法和克鲁斯克尔(Kruskal)算法

一、图的最小生成树

最小生成树(Minimum spanning tree,MST)是最小权重生成树(Minimum weight spanning tree)的简称,是一个连通加权无向图中一棵权值最小的生成树。

在一给定的无向图 G = ( V , E ) G = (V, E) G=(V,E) ( u , v ) (u,v) (u,v) 代表连接顶点 u 与顶点 v 的边 E = { ( u , v ) ∣ u ∈ V , v ∈ V } E = \{ (u, v) | u \in V, v \in V \} E={(u,v)uV,vV},而 w ( u , v ) ) w(u,v)) w(u,v))代表此边的权重,若存在 T T T E E E 的子集(即 T ⊆ E {\displaystyle T\subseteq E} TE)且 ( V , T ) (V, T) (V,T) 为树,使得:

w ( T ) = ∑ ( u , v ) ∈ T w ( u , v ) {\displaystyle w(T)=\sum _{(u,v)\in T}w(u,v)} w(T)=(u,v)Tw(u,v)

w ( T ) w(T) w(T) 最小,则此 TG 的最小生成树。

一个连通图可能有多个生成树。当图中的边具有权值时,总会有一个生成树的边的权值之和小于或者等于其它生成树的边的权值之和。广义上而言,对于非连通无向图来说,它的每一连通分量同样有最小生成树,它们的并被称为最小生成森林。

  1. 最小生成树在一些情况下可能会有多个。例如,当图的每一条边的权值都相同时,该图的所有生成树都是最小生成树。
  2. 如果图的每一条边的权值都互不相同,那么最小生成树将只有一个。

如下加权无向连通图

在这里插入图片描述
其最小成生树为:
在这里插入图片描述

在这里插入图片描述
求取一张无向图的最小生成树的算法主要有普里姆(Prim)算法个克鲁斯克尔(Kruskal)算法。

二、普里姆(Prim)算法

普里姆算法(Prim Algorithm)是图论中的一种贪心算法,可在一个加权连通图中找到其最小生成树。

1. 算法简介

该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(Robert C. Prim)独立发现;1959年,艾兹赫尔·韦伯·迪杰斯特拉(Edsger W. Dijkstra)再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。

2. 算法思想与步骤

算法思想:从某⼀个顶点开始构建⽣成树;每次将代价最⼩的新顶点纳⼊⽣成树,直到所有顶点都纳⼊为⽌。

算法步骤
输入:一个加权连通图,其中顶点集合为 V {\displaystyle V} V,边集合为 E {\displaystyle E} E
输出:使用集合 V new {\displaystyle V_{\text{new}}} Vnew E new {\displaystyle E_{\text{new}}} Enew 来描述所得到的最小生成树。
初始化 V new = { x } {\displaystyle V_{\text{new}}=\{x\}} Vnew={x},其中 x {\displaystyle x} x 为集合 V {\displaystyle V} V 中的任一节点(起始点), E new = { } {\displaystyle E_{\text{new}}=\{\}} Enew={}

  1. 在集合 E {\displaystyle E} E中选取权值最小的边 ( u , v ) {\displaystyle (u,v)} (u,v),其中 u {\displaystyle u} u为集合 V new {\displaystyle V_{\text{new}}} Vnew中的元素,而 v {\displaystyle v} v则是 V {\displaystyle V} V中没有加入 V new {\displaystyle V_{\text{new}}} Vnew的顶点(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
  2. v {\displaystyle v} v加入集合 V new {\displaystyle V_{\text{new}}} Vnew中,将 ( u , v ) {\displaystyle (u,v)} (u,v)加入集合 E new {\displaystyle E_{\text{new}}} Enew中;
  3. 重复步骤1到步骤2,直到 V new = V {\displaystyle V_{\text{new}}=V} Vnew=V

下图为普里姆(Prim)算法的一个例子:
在这里插入图片描述
上图是一个 V = { v 0 , v 1 , v 2 , v 3 , v 4 , v 5 } V=\{v_0,v_1,v_2,v_3,v_4,v_5\} V={v0,v1,v2,v3,v4,v5} 的无向图,使用Prim算法计算最小生成树。

  1. 首先进行初始化操作,任选一个顶点作为初始顶点,本例子中选择 v 0 v_0 v0,即:将 v 0 {v_0} v0加入到 V n e w V_{new} Vnew,此时 V n e w = { v 0 } {V_{new}=\{v_0\}} Vnew={v0}

  2. 其次以 V n e w = { v 0 } {V_{new}=\{v_0\}} Vnew={v0}为基准,寻找与 v 0 v_0 v0 相邻,边的权值最小的顶点,可以发现边 ( v 0 , v 3 ) (v_0, v_3) (v0,v3) 的权值 w 03 w_{03} w03 为1,是最小的,所以连通 v 0 v_0 v0 v 3 v_3 v3,即:将 v 3 {v_3} v3加入到 V n e w V_{new} Vnew并将 ( v 0 , v 3 ) (v_0, v_3) (v0,v3)加入到 E n e w E_{new} Enew此时 V n e w = { v 0 , v 3 } {V_{new}=\{v_0,v_3\}} Vnew={v0v3} E n e w = { ( v 0 , v 3 ) } {E_{new}=\{(v_0, v_3)\}} Enew={(v0,v3)}

  3. 然后以 V n e w = { v 0 , v 3 } {V_{new}=\{v_0,v_3\}} Vnew={v0v3} 中的两个顶点一起作为基准,发现相邻的顶点中边 ( v 2 , v 3 ) (v_2, v_3) (v2,v3) 的权值 w 23 w_{23} w23为4,是最小的,所以连通 v 2 v_2 v2 v 3 v_3 v3 ,即:将 v 2 {v_2} v2加入到 V n e w V_{new} Vnew并将 ( v 2 , v 3 ) (v_2, v_3) (v2,v3)加入到 E n e w E_{new} Enew,此时 V n e w = { v 0 , v 2 , v 3 } {V_{new}=\{v_0,v_2,v_3\}} Vnew={v0,v2,v3} E n e w = { ( v 0 , v 3 ) , ( v 2 , v 3 ) } {E_{new}=\{(v_0, v_3),(v_2, v_3)\}} Enew={(v0,v3),(v2,v3)}

  4. 接下来以 V n e w = { v 0 , v 2 , v 3 } {V_{new}=\{v_0,v_2,v_3\}} Vnew={v0,v2,v3}作为基准,权值 w 25 w_{25} w25 为2,是最小的一条边,连通边 ( v 2 , v 5 ) (v_2, v_5) (v2,v5) ,即:将 v 5 {v_5} v5 加入到 V n e w V_{new} Vnew 并将 ( v 2 , v 5 ) (v_2, v_5) (v2,v5) 加入到 E n e w E_{new} Enew,此时 V n e w = { v 0 , v 2 , v 3 , v 5 } {V_{new}=\{v_0,v_2,v_3, v_5\}} Vnew={v0,v2,v3,v5} E n e w = { ( v 0 , v 3 ) , ( v 2 , v 3 ) , ( v 2 , v 5 ) } {E_{new}=\{(v_0, v_3), (v_2, v_3),(v_2, v_5)\}} Enew={(v0,v3),(v2,v3),(v2,v5)}

  5. V n e w = { v 0 , v 2 , v 3 , v 5 } {V_{new}=\{v_0,v_2,v_3, v_5\}} Vnew={v0v2,v3,v5} 作为基准, w 13 w_{13} w13为5,连通边 ( v 1 , v 3 ) (v_1, v_3) (v1,v3) ,即:将 v 1 {v_1} v1 加入到 V n e w V_{new} Vnew 并将 ( v 1 , v 3 ) (v_1, v_3) (v1,v3) 加入到 E n e w E_{new} Enew,此时 V n e w = { v 0 , v 1 , v 2 , v 3 , v 5 } {V_{new}=\{v_0,v_1,v_2,v_3, v_5\}} Vnew={v0,v1,v2,v3,v5} E n e w = { ( v 0 , v 3 ) , ( v 2 , v 3 ) , ( v 2 , v 5 ) , ( v 1 , v 3 ) } {E_{new}=\{(v_0, v_3), (v_2, v_3),(v_2, v_5),(v_1, v_3)\}} Enew={(v0,v3),(v2,v3),(v2,v5),(v1,v3)}

  6. 最后连通权值为4的边 ( v 1 , v 4 ) (v_1, v_4) (v1,v4) ,即:将 v 4 {v_4} v4 加入到 V n e w V_{new} Vnew 并将 ( v 1 , v 4 ) (v_1, v_4) (v1,v4) 加入到 E n e w E_{new} Enew,此时 V n e w = { v 0 , v 1 , v 2 , v 3 , v 4 , v 5 } {V_{new}=\{v_0,v_1,v_2,v_3,v_4, v_5\}} Vnew={v0,v1,v2,v3,v4,v5} E n e w = { ( v 0 , v 3 ) , ( v 2 , v 3 ) , ( v 2 , v 5 ) , ( v 1 , v 3 ) , ( v 1 , v 4 ) } {E_{new}=\{(v_0, v_3), (v_2, v_3),(v_2, v_5),(v_1, v_3),(v_1, v_4)\}} Enew={(v0,v3),(v2,v3),(v2,v5),(v1,v3),(v1,v4)}

  7. 对比发现此时 V n e w = V {V_{new}=V} Vnew=V,即说明所有节点都已经连通了,最小生成树计算完毕。

经过以上步骤,通过Prim算法得到了无向图 V = { v 0 , v 1 , v 2 , v 3 , v 4 , v 5 } V=\{v_0,v_1,v_2,v_3,v_4,v_5\} V={v0,v1,v2,v3,v4,v5} 的最小生成树。

三、克鲁斯克尔(Kruskal)算法

克鲁斯克尔算法(英语:Kruskal algorithm)是一种用来查找最小生成树的算法,是贪心算法的应用。

1. 算法简介

克鲁斯克尔算法由美国数学家约瑟夫·克鲁斯克尔(Joseph Bernard Kruskal, Jr.)在1956年发表。贪心算法的应用,克鲁斯克尔算法在图中存在相同权值的边时也有效。

2. 算法思想与步骤

算法思想:每次选择⼀条权值最⼩的边,使这条边的两头连通(原本已经连通的就不选),直到所有结点都连通。

算法步骤

  1. 新建图 G {\displaystyle G} G G {\displaystyle G} G中拥有原图中相同的节点,但没有边;
  2. 将原图中所有的边按权值从小到大排序 ;
  3. 从权值最小的边开始,如果这条边连接的两个节点于图 G {\displaystyle G} G中不在同一个连通分量中,则添加这条边到图 G {\displaystyle G} G中;
  4. 重复3,直至图 G {\displaystyle G} G中所有的节点都在同一个连通分量中。

下图为克鲁斯克尔(Kruskal)算法的一个例子:
在这里插入图片描述
上图是一个 V = { v 0 , v 1 , v 2 , v 3 , v 4 , v 5 } V=\{v_0,v_1,v_2,v_3,v_4,v_5\} V={v0,v1,v2,v3,v4,v5} 的无向图,使用Kruskal算法计算最小生成树。

  1. 首先按照权值将所有的边进行排序,按照顺序的集合 E o r d e r = { ( v 0 , v 3 ) , ( v 2 , v 5 ) , ( v 1 , v 4 ) , ( v 2 , v 3 ) , ( v 3 , v 5 ) , ( v 0 , v 2 ) , ( v 1 , v 3 ) , ( v 0 , v 1 ) , ( v 3 , v 4 ) , ( v 4 , v 5 ) } E_{order} =\{(v_0, v_3),(v_2, v_5),(v_1, v_4),(v_2, v_3),(v_3, v_5),(v_0, v_2),(v_1, v_3),(v_0, v_1),(v_3, v_4),(v_4, v_5)\} Eorder={(v0,v3),(v2,v5),(v1,v4),(v2,v3),(v3,v5),(v0,v2),(v1,v3),(v0,v1),(v3,v4),(v4,v5)},将集合中按照权值排序好点按照顺序一次检查是否符合连通条件。
    在这里插入图片描述

  2. 首先检查发现加入顶点 v 0 v_0 v0 v 3 v_3 v3 的边 ( v 0 , v 3 ) (v_0, v_3) (v0,v3) 后不会构成回路,即当前顶点 v 0 v_0 v0 v 3 v_3 v3 不在同一个连通分量中,则可以将顶点 v 0 v_0 v0 v 3 v_3 v3 连起来,即 E n e w = { ( v 0 , v 3 ) } E_{new} =\{(v_0, v_3)\} Enew={(v0,v3)}

  3. 依次检查发现加入顶点 v 2 v_2 v2 v 3 v_3 v3 的边 ( v 2 , v 3 ) (v_2, v_3) (v2,v3) 后不会构成回路,即当前顶点 v 2 v_2 v2 v 3 v_3 v3 不在同一个连通分量中,则可以将顶点 v 2 v_2 v2 v 3 v_3 v3 连起来,即 E n e w = { ( v 0 , v 3 ) , ( v 2 , v 5 ) } E_{new} =\{(v_0, v_3),(v_2, v_5)\} Enew={(v0,v3),(v2,v5)}

  4. 依次检查发现加入顶点 v 1 v_1 v1 v 4 v_4 v4 的边 ( v 1 , v 4 ) (v_1, v_4) (v1,v4) 后不会构成回路,即当前顶点 v 1 v_1 v1 v 4 v_4 v4 不在同一个连通分量中,则可以将顶点 v 1 v_1 v1 v 4 v_4 v4 连起来,即 E n e w = { ( v 0 , v 3 ) , ( v 2 , v 5 ) , ( v 1 , v 4 ) } E_{new} =\{(v_0, v_3),(v_2, v_5),(v_1, v_4)\} Enew={(v0,v3),(v2,v5),(v1,v4)}

  5. 依次检查发现加入顶点 v 2 v_2 v2 v 3 v_3 v3 的边 ( v 2 , v 3 ) (v_2, v_3) (v2,v3) 后不会构成回路,即当前顶点 v 2 v_2 v2 v 3 v_3 v3 不在同一个连通分量中,则可以将顶点 v 2 v_2 v2 v 3 v_3 v3 连起来,即 E n e w = { ( v 0 , v 3 ) , ( v 2 , v 5 ) , ( v 1 , v 4 ) , ( v 2 , v 3 ) } E_{new} =\{(v_0, v_3),(v_2, v_5),(v_1, v_4),(v_2, v_3)\} Enew={(v0,v3),(v2,v5),(v1,v4),(v2,v3)}

  6. 依次检查发现加入顶点 v 3 v_3 v3 v 5 v_5 v5 的边 ( v 3 , v 5 ) (v_3, v_5) (v3,v5) 后会构成回路,即当前顶点 v 3 v_3 v3 v 5 v_5 v5 在同一个连通分量中,则不能将顶点 v 3 v_3 v3 v 5 v_5 v5 连起来;

  7. 依次检查发现加入顶点 v 0 v_0 v0 v 2 v_2 v2 的边 ( v 0 , v 2 ) (v_0, v_2) (v0,v2) 后会构成回路,即当前顶点 v 0 v_0 v0 v 2 v_2 v2 在同一个连通分量中,则不能将顶点 v 0 v_0 v0 v 2 v_2 v2 连起来;

  8. 依次检查发现加入顶点 v 1 v_1 v1 v 3 v_3 v3 的边 ( v 1 , v 3 ) (v_1, v_3) (v1,v3) 后不会构成回路,即当前顶点 v 1 v_1 v1 v 3 v_3 v3 不在同一个连通分量中,则可以将顶点 v 1 v_1 v1 v 3 v_3 v3 连起来,即 E n e w = { ( v 0 , v 3 ) , ( v 2 , v 5 ) , ( v 1 , v 4 ) , ( v 2 , v 3 ) , ( v 1 , v 3 ) } E_{new} =\{(v_0, v_3),(v_2, v_5),(v_1, v_4),(v_2, v_3),(v_1, v_3)\} Enew={(v0,v3),(v2,v5),(v1,v4),(v2,v3),(v1,v3)}

  9. 此时所有顶点均已处于同一个连通分量中,再连上任意一条边都会形成回路,所以最小生成树计算完毕!

四、普里姆(Prim)算法对比克鲁斯克尔(Kruskal)算法

  • 算法思想:
    • 普里姆(Prim)算法:从某⼀个顶点开始构建⽣成树;每次将代价最⼩的新顶点纳⼊⽣成树,直到所有顶点都纳⼊为⽌。
    • 克鲁斯克尔(Kruskal)算法:每次选择⼀条权值最⼩的边,使这条边的两头连通(原本已经连通的就不选)直到所有结点都连通
  • 时间复杂度:
    • 普里姆(Prim)算法: O ( ∣ V ∣ 2 ) O(|V|^2 ) O(V2)
    • 克鲁斯克尔(Kruskal)算法: O ( ∣ E ∣ l o g 2 ∣ E ∣ ) O( |E|log_2 |E| ) O(Elog2E)
  • 适用范围:
    • 普里姆(Prim)算法:适合⽤于边稠密图
    • 克鲁斯克尔(Kruskal)算法:适合⽤于边稀疏图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/897029.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

win11环境下成功安装mamba

文章目录 1. Mamba环境搭建2. triton安装3. causal_conv1d安装3.1 下载causal_conv1d工程文件源码3.2 修改setup.py文件3.3 安装 causal_conv1d 4. Mamba安装4.1 下载mamba工程文件源码4.2 修改setup.py文件4.3 安装 mamba 5. 查看所有成功安装的库6. 测试mamba安装是否成功6.1…

软件质量管理体系,软件评审资料,资质认证资料,安全建设,数据安全及项目管理全套资料(原件参考)

软件项目质量管理体系是指一套系统化的管理方法、流程、工具和文档,旨在确保软件项目从需求分析、设计、开发、测试到部署和维护的整个生命周期中,都能达到预定的质量标准和客户期望。该体系通过明确的角色和责任、标准化的工作流程、有效的质量控制和持…

MySQL(python开发)——(3)表数据的基本操作,增删改查

MySQL(python开发)——(1)数据库概述及其MySQL介绍 MySQL(python开发)——(2)数据库基本操作及数据类型 MySQL—— 表数据基本操作 一、表中插入(insert)数据——增 insert into 表名 values (值1&#…

springboot2.0x 和springboot 1.0 整合redis 使用自定义CacheManager 问题

问题描述: 在我们深入理解springboot2.0x的缓存机制的时候,发现在springboot1.0 和springboot2.0 中默认的序列化都是使用的jdk的 Serializer 实现这个接口,jdk自带的序列化方法,由此我们需要自己去创建自定义的RedisCacheManager…

解决springboot redisTemplate lua execute hash脚本 field有转义符的问题

问题:使用execute,是 result redisTemplate.execute(redisScript,Collections.singletonList(hashKey), // KEYSargs.toArray()); // ARGV会存在field有转义符 发现这个方法是直接调用下图的方法 使用的序列化的方式是 template.getValueSerializer(…

详解23种设计模式——第一部分:概述+创建型模式

目录 1. 概述 2. 创建型模式 2.1 简单(静态)工厂模式 2.1.1 介绍 2.1.2 实现 2.2 工厂模式 2.3 抽象工厂模式 2.4 单例模式 2.4.1 饿汉模式 2.4.2 懒汉模式 2.4.3 线程安全的懒汉式 2.4.4 DCL单例 - 高性能的懒汉式 2.5 建造者模式 2.6 原…

【进阶OpenCV】 (19)-- Dlib库 --人脸表情识别

文章目录 表情识别一、原理二、代码实现1. 摄像头前预处理2. 计算嘴唇变化3. 绘制嘴唇轮廓4. 显示结果5. 完整代码展示 总结 表情识别 目标:识别人物的喜悦状态。 一、原理 我们在对一张人脸图片进行关键点定位后,得到每个关键点的位置: 比…

go压缩的使用

基础:使用go创建一个zip func base(path string) {// 创建 zip 文件zipFile, err : os.Create("test.zip")if err ! nil {panic(err)}defer zipFile.Close()// 创建一个新的 *Writer 对象zipWriter : zip.NewWriter(zipFile)defer zipWriter.Close()// 创…

【Linux】————动静态库

作者主页: 作者主页 本篇博客专栏:Linux 创作时间 :2024年10月22日 一.库的定义 什么是库,在windows平台和linux平台下都大量存在着库。 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载…

渗透实战 JS文件怎么利用

1.前言 关于JS在渗透测试中的关键作用,想必不用过多强调,在互联网上也有许多从JS中找到敏感信息从而拿下关键系统的案例。大部分师傅喜欢使用findsomething之类的浏览器插件,也有使用诸如Unexpected.information以及APIFinder之类的Burp插件…

ES6 Promise的用法

学习链接:ES6 Promise的用法,ES7 async/await异步处理同步化,异步处理进化史_哔哩哔哩_bilibili 一、同步与异步区别 1.JavaScript代码是单线程的程序,即通过一行一行代码顺序执行,即同步概念。 2.若处理一些简短、…

算法魅力-双指针的实战

目录 1.双指针的介绍 1. 左右指针(对撞指针) 2. 快慢指针 2.题目练习讲解 2.1 移动零 算法思路 代码展示 画图效果效果 2.2 复写零 算法思路 代码展示 2.3 快乐数 算法思路 代码展示 2.4 盛最多水的容器 算法思路 代码展示 结束语 1.双指针的…

宝塔PHP8.1安装fileinfo拓展失败解决办法

在宝塔面板中安装PHP8.1后,安装fileinfo扩展一直安装不上,查看日志有报错,于是手动来安装也报错。 宝塔报错: 手动命令行编译安装同,也有报错 cd /www/server/php/81/src/ext/fileinfo/ make distclean ./configure …

【用74ls194实现1000-0100-0010-0001转换】2022-5-13

试用74194附加门电路设计1011011010序列发生器,并用示波器观察。要求:(1)写出设计过程;(2)画出电路图。 2、用multisim软件仿真实现上述序列信号发生器,CP频率为1KHz,用示…

【HarmonyOS】应用实现APP国际化多语言切换

【HarmonyOS】应用实现APP国际化多语言切换 前言 在鸿蒙中应用国际化处理,与Android和IOS基本一致,都是通过JSON配置不同的语言文本内容。在UI展示时,使用JSON配置的字段key进行调用,系统选择对应语言文本内容。 跟随系统多语言…

【scene_manager】与 MoveIt 机器人的规划场景进行交互

scene_manager Scene Manager包是由 Robotnik 创建的 ROS 包,旨在帮助构建和与 MoveIt 机器人的规划场景进行交互。 背景信息 MoveIt 规划场景 是一个用于存储机器人周围世界的表示(外部碰撞)以及机器人自身状态(内部碰撞和当…

rollup.js 插件实现原理与自定义

Rollup.js 是一个JavaScript模块打包器,它主要用于将小块代码编译成大块复杂的库或应用程序。相较于Webpack,Rollup更专注于代码的ES模块转换和优化,特别适合构建库或者那些对代码体积、执行效率有严格要求的应用。Rollup的核心特性之一就是它…

NETSH端口转发

NETSH介绍 netsh是windows系统自带命令行程序,攻击者无需上传第三方工具即可利用netsh程序可进行端口转 发操作,可将内网中其他服务器的端口转发至本地访问运行这个工具需要管理员的权限 实验场景 现在有如下的网络,电脑A是攻击机器&#x…

衡石分析平台系统分析人员手册-仪表盘控件概述

控件​ 控件是仪表盘的基本组成单位。控件种类很多,有展示分析数据的图表类类控件,有展示图片、文字的展示类控件,还有可导出数据、刷新数据、过滤数据等功能类控件。一个完整的仪表盘由多种不同功能的控件构成。 控件类型​ 根据控件是否展…

10月18日笔记(基于系统服务的权限提升)

系统内核漏洞提权 当目标系统存在该漏洞且没有更新安全补丁时,利用已知的系统内核漏洞进行提权,测试人员往往可以获得系统级别的访问权限。 查找系统潜在漏洞 手动寻找可用漏洞 在目标主机上执行以下命令,查看已安装的系统补丁。 system…