最小生成树,秒懂!

article/2025/9/16 9:30:22

👇👇关注后回复 “进群” ,拉你进程序员交流群👇👇

作者丨bigsai

来源丨bigsai

前言

在数据结构与算法的图论中,(生成)最小生成树算法是一种常用并且和生活贴切比较近的一种算法。但是可能很多人对概念不是很清楚,什么是最小生成树?

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

通俗易懂的讲就是最小生成树包含原图的所有节点而只用最少的边最小的权值距离。因为n个节点最少需要n-1个边联通,而距离就需要采取某种策略选择恰当的边。

学习最小生成树实现算法之前我们要先高清最小生成树的结构和意义所在。咱么首先根据一些图更好的祝你理解。

一个故事

在城市道路规划中,是一门很需要科学的研究(只是假设学习不必当真)。在公路时代城市联通的主要矛盾是时间慢,而造价相比运输时间是次要矛盾。所以在公路时代我们尽量使得城市能够直接联通,缩短城市联系时间,而稍微考虑建路成本!随着科技发展、高级铁路、信息传输相比公路运输快非常非常多,从而事件的主要矛盾从运输时间转变为造价成本,故有时会关注联通所有点的路程(最短),这就用到最小生成树算法。

城市道路铺设可能经历以下几个阶段。

  • 初始,各个城市没有高速公路(铁路)。

  • 政府打算各个城市铺设公路(铁路),每个城市都想成为交通枢纽,快速到达其他城市,但每个城市都有这种想法,如果实现下去造价太昂贵。并且造成巨大浪费。

  • 最终国家选择一些主要城市进行联通,有个别城市只能稍微绕道而行,而绕道太远的、人流量多的国家考虑新建公路(铁路),适当提高效率。

3b8fe901dece52a8af03e4a3b7f7e934.png

不过上面铁路规划上由于庞大的人口可能不能够满足与"有铁路"这个需求,人们对速度、距离、直达等条件一直在追求中……

但是你可以想象这个场景:有些东西造假非常非常昂贵,使用效率非常高,我这里假设成黄金镶钻电缆 铺设,所以各个城市只要求不给自己落下,能通上就行(没人敢跳了吧)。

要从有环图中选取代价和最小的路线一方面代价最小(总距离最小最省黄金)另一方面联通所有城市

然而根据上图我们可以得到以下最小生成树,但是最么生成这个最小生成树,就是下面要讲的了。

c75f1c104b939bc7c97fe97e6df4d7da.png

而类似的还有局部区域岛屿联通修桥海底通道这些高成本的都多多少少会运用。

Kruskal算法

上面介绍了最小生成树是什么,现在需要掌握和理解最小生成树如何形成。给你一个图,用一个规则生成一个最小生成树。而在实现最小生成树方面有prim和kruskal算法,这两种算法的策略有所区别,但是时间复杂度一致。

Kruskal算法,和前面讲到的并查集关系很大,它的主要思想为:

先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。

简而言之,Kruskal算法进行调度的单位是边,它的信仰为:所有边能小则小,算法的实现方面要用到并查集判断两点是否在同一集合。

而算法的具体步骤为:

  1. 将图中所有边对象(边长、两端点)依次加入集合(优先队列)q1中。初始所有点相互独立

  2. 取出集合(优先队列)q1最小边,判断边的两点是否联通。

  3. 如果联通说明两个点已经有其它边将两点联通了,跳过,如果不连通,则使用union(并查集合并)将两个顶点合并,这条边被使用(可以储存或者计算数值)。

  4. 重复2,3操作直到集合(优先队列)q1为空。此时被选择的边构成最小生成树。

56fb9a6454ef3cb5bd77457f8f1bed72.png

06d895a19ada789d701cd9c9489702da.png

Prim算法

除了Kruskal算法以外,普里姆算法(Prim算法)也是常用的最小生成树算法。虽然在效率上差不多。但是贪心的方式和Kruskal完全不同。

prim算法的核心信仰是:从已知扩散寻找最小。它的实现方式和Dijkstra算法相似但稍微有所区别,Dijkstra是求单源最短路径,而每计算一个点需要对这个点重新更新距离,而prim不用更新距离。直接找已知点的邻边最小加入即可!primkruskal算法都是从边入手处理。

对于具体算法具体步骤,大致为:

  1. 寻找图中任意点,以它为起点,它的所有边V加入集合(优先队列)q1,设置一个boolean数组bool[]标记该位置(边有两个点,每次加入没有被标记那个点的所有边)。

  2. 从集合q1找到距离最小的那个边v1并 判断边是否存在未被标记的一点`p` ,如果p不存在说明已经确定过那么跳过当前边处理,如果未被标(访问)记那么标记该点p,并且与p相连的未知点(未被标记)构成的边加入集合q1, 边v1(可以进行计算距离之类,该边构成最小生成树) .

  3. 重复1,2直到q1为空,构成最小生成树 !

大体步骤图解为:

a2665cdbd3687dc0ba241320e9dd5323.png

952182544fe2fd5a5497434e2d93059f.png

因为prim从开始到结束一直是一个整体在扩散,所以不需要考虑两棵树合并的问题,在这一点实现上稍微方便了一点。

当然,要注意的是最小生成树并不唯一,甚至同一种算法生成的最小生成树都可能有所不同,但是相同的是无论生成怎样的最小生成树:

  • 能够保证所有节点连通(能够满足要求和条件)

  • 能够保证所有路径之和最小(结果和目的相同)

  • 最小生成树不唯一,可能多样的

95c1715bcfce41f7af1843be77dd3427.png

代码实现

上面分析了逻辑实现。下面我们用代码简单实现上述的算法。

prim

package 图论;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;public class prim {public static void main(String[] args) {int minlength=0;//最小生成树的最短路径长度int max=66666;String cityname[]= {"北京","武汉","南京","上海","杭州","广州","深圳"};int city[][]= {{ max, 8, 7, max, max, max, max }, //北京和武汉南京联通{ 8, max,6, max,9, 8,max }, //武汉——北京、南京、杭州、广州{ 7, 6, max, 3,4, max,max }, //南京——北京、武汉、上海、杭州{ max, max,3, max,2, max,max }, //上海——南京、杭州{ max, 9,4, 2,max, max,10 }, //杭州——武汉、南京、上海、深圳{ max, 8,max, max,max, max,2 }, //广州——武汉、深圳{ max, max,max, max,10,2,max }//深圳——杭州、广州};// 地图boolean istrue[]=new boolean[7];//南京Queue<side>q1=new PriorityQueue<side>(new Comparator<side>() {public int compare(side o1, side o2) {// TODO Auto-generated method stubreturn o1.lenth-o2.lenth;}});for(int i=0;i<7;i++){if(city[2][i]!=max){istrue[2]=true;q1.add(new side(city[2][i], 2, i));}}       while(!q1.isEmpty()){side newside=q1.poll();//抛出if(istrue[newside.point1]&&istrue[newside.point2]){continue;}else {if(!istrue[newside.point1]){istrue[newside.point1]=true;minlength+=city[newside.point1][newside.point2];System.out.println(cityname[newside.point1]+" "+cityname[newside.point2]+" 联通");for(int i=0;i<7;i++){if(!istrue[i]){q1.add(new side(city[newside.point1][i],newside.point1,i));}}}else {istrue[newside.point2]=true;minlength+=city[newside.point1][newside.point2];System.out.println(cityname[newside.point2]+" "+cityname[newside.point1]+" 联通");for(int i=0;i<7;i++){if(!istrue[i]){q1.add(new side(city[newside.point2][i],newside.point2,i));}}}}}System.out.println(minlength);      }static class side//边{int lenth;int point1;int point2;public side(int lenth,int p1,int p2) {this.lenth=lenth;this.point1=p1;this.point2=p2;}}}

输出结果:

上海 南京 联通
杭州 上海 联通
武汉 南京 联通
北京 南京 联通
广州 武汉 联通
深圳 广州 联通
28

Kruskal:
package 图论;import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;import 图论.prim.side;
/** 作者:bigsai(公众号)*/
public class kruskal {static int tree[]=new int[10];//bing查集public static void init() {for(int i=0;i<10;i++)//初始{tree[i]=-1;}}public static int search(int a)//返回头节点的数值{if(tree[a]>0)//说明是子节点{return tree[a]=search(tree[a]);//路径压缩}elsereturn a;}public static void union(int a,int b)//表示 a,b所在的树合并小树合并大树(不重要){int a1=search(a);//a根int b1=search(b);//b根if(a1==b1) {//System.out.println(a+"和"+b+"已经在一棵树上");}else {if(tree[a1]<tree[b1])//这个是负数,为了简单减少计算,不在调用value函数{tree[a1]+=tree[b1];//个数相加  注意是负数相加tree[b1]=a1;       //b树成为a的子树,直接指向a;}else{tree[b1]+=tree[a1];//个数相加  注意是负数相加tree[a1]=b1;       //b树成为a的子树,直接指向a;}}}public static void main(String[] args) {// TODO Auto-generated method stubinit();int minlength=0;//最小生成树的最短路径长度int max=66666;String cityname[]= {"北京","武汉","南京","上海","杭州","广州","深圳"};boolean jud[][]=new boolean[7][7];//加入边需要防止重复 比如 ba和ab等价的int city[][]= {{ max, 8, 7, max, max, max, max }, { 8, max,6, max,9, 8,max }, { 7, 6, max, 3,4, max,max }, { max, max,3, max,2, max,max }, { max, 9,4, 2,max, max,10 }, { max, 8,max, max,max, max,2 }, { max, max,max, max,10,2,max }};// 地图boolean istrue[]=new boolean[7];//南京Queue<side>q1=new PriorityQueue<side>(new Comparator<side>() {//优先队列存边+public int compare(side o1, side o2) {// TODO Auto-generated method stubreturn o1.lenth-o2.lenth;}});for(int i=0;i<7;i++){for(int j=0;j<7;j++){if(!jud[i][j]&&city[i][j]!=max)//是否加入队列{jud[i][j]=true;jud[j][i]=true;q1.add(new side(city[i][j], i, j));}}}while(!q1.isEmpty())//执行算法{side newside=q1.poll();int p1=newside.point1;int p2=newside.point2;if(search(p1)!=search(p2)){union(p1, p2);System.out.println(cityname[p1]+" "+cityname[p2]+" 联通");minlength+=newside.lenth;}}System.out.println(minlength);}static class side//边{int lenth;int point1;int point2;public side(int lenth,int p1,int p2) {this.lenth=lenth;this.point1=p1;this.point2=p2;}}
}

输出结果

上海 杭州 联通
广州 深圳 联通
南京 上海 联通
武汉 南京 联通
北京 南京 联通
武汉 广州 联通
28

总结

最小生成树算法理解起来也相对简单,实现起来也不是很难。Kruskal和Prim主要是贪心算法的两种角度。一个从整体开始找最小边,遇到关联不断合并,另一个从局部开始扩散找身边的最小不断扩散直到生成最小生成树。在学习最小生成树之前最好学习一下dijkstra算法和并查集,这样在实现起来能够快一点,清晰一点。

力扣1584就是一个最小生成树的入门题,不过哪个有点区别的就是默认所有点是联通的,所以需要你剪枝优化。这里就不带大家一起看啦,有问题下面也可交流!

最后,如果你那天真的获得一大笔资金去修建这么一条昂贵的黄金路线,可以适当采取此方法,另外剩下的大批,苟富贵,勿相忘

-End-

最近有一些小伙伴,让我帮忙找一些 面试题 资料,于是我翻遍了收藏的 5T 资料后,汇总整理出来,可以说是程序员面试必备!所有资料都整理到网盘了,欢迎下载!

0e7534d0b8193d597bf05742e21b04a3.png

点击👆卡片,关注后回复【面试题】即可获取

在看点这里745a34e6444af4e10fe2f90aec940a93.gif好文分享给更多人↓↓


http://chatgpt.dhexx.cn/article/mBtqPexw.shtml

相关文章

算法 - 最小生成树实现

算法能力是一个门槛&#xff0c;也是个有基础的门槛 无论你是iOS工程师&#xff0c;android工程师&#xff0c;java工程师&#xff0c;前端&#xff0c;后端还是全栈等等… 算法能力的强弱一方面在于思想&#xff0c;你是否有计算机思维抽象具体问题的能力 更重要的还在与基…

最小树形图(有向图的最小生成树)

我们知道&#xff0c;无向图的最小生成树的求法有Krusal和prime算法&#xff0c;一个是归点一个是归边&#xff0c;在具体实现上Krusal可以用并查集实现&#xff0c;难度不大。 这里稍微区别一下最短路径和最小生成树&#xff08;因为我又搞混了23333&#xff09; 最小生成树能…

Kruskal算法(最小生成树)

上篇Prim算法简要的讲解了最小生成树。也提到过Prim算法堆优化&#xff0c;但本蒟蒻并没有贴Prim &#xff08;堆优化的代码&#xff09;。至于为什么没有贴呢&#xff1f;上篇Prim算法blog末尾有说明。 好勒&#xff01;咱们接着讲Kruskal算法。这跟Prim算法有很大的…

最小生成树matlab求解

一、最小生成树 连通所有顶点且总路径最小修建连通7个城市的铁路网&#xff0c;可修建的路线和对应造价如图所示&#xff0c;如何修建使总费用最少&#xff1f; 问题分析&#xff1a; 连通7个城市&#xff1a;生成的图中&#xff0c;从任意顶点起步&#xff0c;沿着边一定可以…

最小生成树——贪心算法

文章目录 1.生成树和最小生成树1.1 问题的定义1.2 MST性质 2.普里姆算法&#xff08;Prim&#xff09;2.1 算法流程2.2 算法正确性证明2.3 算法实现2.4 时间复杂度2.5 测试代码 3.克鲁斯卡尔算法&#xff08;kruskal&#xff09;3.1 算法流程3.2 算法正确性证明3.3 算法实现 参…

C++ 最小生成树

目录&#xff1a; 前置知识 最小生成树 Prim 算法 kruskal 算法 前置知识&#xff1a; 连通图&#xff1a;在无向图中&#xff0c;若任意两个顶点 u 与 v 都有路径相通&#xff0c;则称该无向图为连通图。 强连通图&#xff1a;在有向图中&#xff0c;若任意两个顶点 u 与 …

最小生成树(C语言实现)

求这个网的最小生成树 /** 普里姆算法和克鲁斯卡尔算法求最小生成树* 采用邻接矩阵存储**/ #include<stdio.h>#define MAX_VERTEX_NUM 20 //图的定义 typedef struct {int vertexNum;int edgeNum;char vertex[MAX_VERTEX_NUM];int arc[MAX_VERTEX_NUM][MAX_VERTEX_NUM]…

最小生成树(C语言)

最小生成树问题&#xff08;C语言&#xff09; 所谓一个 带权图 的最小生成树&#xff0c;就是原图中边的权值最小的生成树 &#xff0c;所谓最小是指边的权值之和小于或者等于其它生成树的边的权值之和。 kruskal 克鲁斯卡尔算法&#xff08;Kruskal&#xff09;是一种使用贪…

最小生成树kruskal算法

最小生成树kruskal算法 概述算法分析代码 概述 克鲁斯卡尔 ( K r u s k a l ) (Kruskal) (Kruskal)算法是求连通网的最小生成树的另一种方法。与普里姆 ( P r i m ) (Prim) (Prim)算法不同&#xff0c;它的时间复杂度为 O ( e l o g e ) O(eloge) O(eloge)(e为网中的边数)&…

最小生成树:

定义&#xff1a; 将图中的 n 结点用 n-1 条边连接起来&#xff0c;使其各个边的权值之和最小的生成树。 普里姆算法&#xff1a; 从点的角度出发。 1、start 数组的值表示起始点的下标&#xff0c;start 的下标代表目的顶点&#xff1b;lowcost 数组的下标代表目的顶点&…

图——最小生成树

图——最小生成树 1. 基本概念 在一个连通无向图G(V, E)中&#xff0c;对于其中的每条边(u,v)∈E&#xff0c;赋予其权重w(u, v)&#xff0c;则最小生成树问题就是要在G中找到一个连通图G中所有顶点的无环子集T⊆E&#xff0c;使得这个子集中所有边的权重之和w(T) ∑ ( u , …

最小生成树的Kruskal算法-详解

最小生成树的Kruskal算法 一、 什么是最小生成树 1.1 最小生成树定义&#xff1a; 一个有 n 个结点的连通图的生成树是原图的极小连通子图&#xff0c;且包含原图中的所有 n 个结点&#xff0c;并且有保持图连通的最少的边。最小生成树可以用kruskal&#xff08;克鲁斯卡尔&a…

最小生成树(C语言简单实现)

最小生成树 本文参考自《大话数据结构》 一个连通图的生成树是一个极小的连通子图&#xff0c;它含有图中全部的顶点&#xff0c;但只有足以构成一棵树的n-1条边。我们把构造连通网的最小代价生成树称为最小生成树 。 找连通网的最小生成树&#xff0c;经典的有两种算法&#…

最小生成树怎么画?

介绍最小生成树的图形画法~以下图为例&#xff1a; 1、从1开始&#xff0c;以1为顶点画圈。在红色线经过的部分中&#xff0c;可见权重分别为6、1、5&#xff0c;最小权重为1。如下图。 2、以上图中得到的1、3为顶点的图中&#xff0c;继续画线&#xff0c;图为黄色线部分。经过…

最小生成树算法

最小生成树算法 生成树的概念最小生成树算法Prim算法Kruskal算法 生成树的概念 若图是连通的无向图或强连通的有向图&#xff0c;则从其中任一顶点出发&#xff0c;调用一次 d f s dfs dfs或者 b f s bfs bfs后&#xff0c;可以系统的访问图中所有顶点。若图是有根的有向图 &a…

【图】最小生成树

最小生成树&#xff1a;构造连通网的最小代价生成树。 最小生成树有两种算法&#xff1a;普利姆算法、克鲁斯卡尔算法。 普利姆&#xff08;Prim&#xff09;算法 加点&#xff0c;选择相邻点中边权最小的 需要两个一维数组&#xff0c;一个存权值&#xff0c;另一个存起始点…

最小生成树-Kruskal算法

数据结构:最小生成树-Kruskal算法 什么是最小生成树&#xff0c;最小生成树就是求图中把所有点都访问到的最短路径的长度 Kruskal算法采用的是边贪心思想&#xff0c;我们先大概讲一下它的大概思想&#xff0c;首先我们先假设先隐藏所有的边&#xff0c;这样每个点会成为一个连…

最小生成树(kruskal算法)

一、概述 最小生成树问题顾名思义&#xff0c;概括来说就是路修的最短。 接下来引入几个一看就明白的定义&#xff1a; 最小生成树相关概念&#xff1a; 带权图&#xff1a;边赋以权值的图称为网或带权图&#xff0c;带权图的生成树也是带权的&#xff0c;生成树T各边的权值…

最小生成树、最短路径树

一、最小生成树与最短路径树的区别 最小生成树能够保证整个拓扑图的所有路径之和最小&#xff0c;但不能保证任意两点之间是最短路径。 应用如网络部线&#xff0c;把所有的电脑(服务器&#xff1f;&#xff09;都连起来用的网线(光纤&#xff1f;&#xff09;最少&#xff0c…

【图解算法】最小生成树

今天我们介绍图论中的另一部分&#xff0c;最小生成树。 对图的最短路感兴趣的同学可以看&#xff1a; 【图解算法】一次解决最短路径问题 目录 1. 最小生成树简介2. Prim算法2.1 模板题2.2 思路模板2.3 代码实现2.3 prim 算法的优化 3. Kruskal算法3.1 模板题3.2 思路模板3.3…