C++ 最小生成树

article/2025/9/16 10:11:33

目录:

前置知识

最小生成树

Prim 算法

kruskal 算法


前置知识:

        连通图:在无向图中,若任意两个顶点 u 与 v 都有路径相通,则称该无向图为连通图。

        强连通图:在有向图中,若任意两个顶点 u 与 v 都有路径相通,则称该有向图为强连通图。

        连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。

        生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的 n-1 条边。一颗有n个顶点的生成树有且仅有 n-1 条边,如果生成树中再添加一条边,则必定成环。

        最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。 

最小生成树:

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

        简而言之,最小生成树就是一颗由 n 个点、n-1 条边且这 n-1 条边所组成的边权和最小。

Prim 算法: 

        Prim 算法又称DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。

        Prim 算法是一种贪心,Prim 最初的时候是将所有的点分为两个集合  V_{a} 、 V_{b} ,其中一个集合V_{a} 表示已经加入生成树的点,而另一个集合 V_{b} 表示还未加入生成树的点。初始时,V_{a}为空(当然,也可以只包含一个节点),其他节点都在  V_{b} 。每次选从 V_{b} 中选取一个节点加入V_{a}  ,同时保证这一节点与 V_{a} 中的某一个节点的边长最小,显然这条边就是最小生成树上的边,直到所有的节点都被插入生成树中,算法即结束,所到得的生成树就是最小生成树。

        显然,这种策略是正确的。

        因此,Prim 算法的时间复杂度O(n^{2 }) 。

        图文解释:

 【代码实现】

int n;//点数 
bool v[1001];//记录该点是否被放入生成树 
int a[1001][1001];//原图数据 
int dis[1001];//每个点到生成树的距离 
for(int i=1;i<=n;i++)
{int p=0;int minn=0x7f7f7f;//初始为无穷大 for(int j=1;j<=n;j++)//寻找最短距离 {if(!v[j]&&dis[j]<minn){minn=dis[j];p=i;}}v[p]=1;ans+=minn;//加入生成树并统计边权和 for(int i=1;i<=n+1;i++)//更新每个点到生成树的最小距离 {if(dis[i]>a[p][i]){dis[i]=a[p][i];}}
}

       

         这里为大家引入一道的 Prim 算法的例题:

         解题思路:目标是在一个点上建立一所发电站,并让所有的矿井都有电可用。这样一来,就可以引入一个新的知识——超级点。可以设不存在的 0 号点作为超级点,在矿井上建立发电站相当于把它与 0 号点相连,这样以来,问题就转化成了一个最小生成树问题,然后,就是套用 Prim 的模板。

【AC 代码】

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<string>
#include<cstring>
#include<queue>//头文件
using namespace std;
int n,m,ans;
int minn;
int dis[2001],k;
bool v[2001];
int f[2001][2001];//变量的声明
int main()
{scanf("%d",&n);//读入for(int i=1;i<=n;i++){int x;scanf("%d",&x);//读入f[i][0]=x;//设置0号点f[0][i]=x;//设置0号点}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){scanf("%d",&f[i][j]);//读入}}for(int i=1;i<=n;i++){f[i][i]=0x7f7f7f;//自己与自己不能相连,所以初始为无穷大}v[0]=true;//0号点放入生成树for(int i=1;i<=n;i++){dis[i]=f[0][i];//初始与生成树的最小距离}for(int i=1;i<=n;i++){minn=0x7f7f7f;k=0;for(int j=1;j<=n;j++)//寻找距离最短且不在生成树上的点{if((!v[j])&&(dis[j]<minn)){minn=dis[j];k=j;}}ans+=dis[k];//加入生成树,并更新权值v[k]=true;//标记for(int j=1;j<=n;j++)//更新每个点到生成树的最短距离{if(f[k][j]<dis[j]){dis[j]=f[k][j];}}}printf("%d\n",ans);//输出return 0;//完结撒花} 

 kruskal 算法:

        

        kruskal 算法也是一种贪心算法,只不过 Prim 算法是对生成树上的点所连的边贪心,而kruskal 算法是对所有的边进行贪心。

        kruskal 算法的策略是:先把每个节点当成一颗最小生成树,然后按照边权从小到大排序,每次枚举边权最小的边,如果两个点不在同一棵最小生成树中,就将两棵生成树合并,反之则舍弃,直到插入 n-1 条边形成一珂生成树。得到的生成树就是最小生成树。

        kruskal 算法与并查集有惊人的相似,所以建议先学会并查集。

        显然,这种策略是正确的。

        因此,kruskal 算法的时间复杂度是:O(m×log m) 。

        图文解释:

 【代码实现】

int n,m;//n个点,m条边
int sum,ans;//sum记录连接的边数
int fa[1000001];//记录其祖先
struct node//结构体
{int x;//端点之一int y;//端点之二int w;//边权值
}a[1000001];
int cmp(node x,node y)//将数据按照从小到大的顺序排序
{return x.w<y.w;
}
int find(int k)//寻找k的祖先
{if(fa[k]==k){return k;}return fa[k]=find(fa[k]);
}
for(int i=1;i<=n;i++)//初始化
{fa[i]=i;
}
sort(a+1,a+1+m,cmp);//排序
for(int i=1;i<=m;i++)//加边
{int x=find(a[i].x);//找端点之一的祖先int y=find(a[i].y);//找端点之二的祖先if(x!=y)//如果不在同一个集合{fa[y]=x;//合并sum++;//边数加1ans+=a[i].w;//记录边权和if(sum==n-1)//如果边数达到n-1,则寻找完毕{break;/*/跳出循环}} 
}

        

        小编为大家引入一道经典的 kruskal 的题目,以便大家巩固。

        题目传送名:P2330 [SCOI2005]繁忙的都市

        解题思路:其实这道题不论是用 Prim 还是 kruskal 都很简单,不过这里主要以练习 kruskal 为主要目的,所以,重点讲 kruskal 算法。

        题目要求的是最少的边数和生成树内最大的边权值,边的数量肯定是 n-1 ,最大的边权值也只需要在跑 kruskal 的时候记录即可。

【代码实现】

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<string>
#include<cstring>
#include<queue>//头文件
using namespace std;
int n,m;//n个点,m条边
int sum,ans;//sum用来记录加入生成树的边的数量,ans用来更新最大的边权值
int fa[1000001];//记录祖先节点
struct node//记录每条边的信息
{int x;int y;int w;//边的权值
}a[1000001];
int cmp(node x,node y)//将所有的边按照从小到大的顺序排列
{return x.w<y.w;
}
int find(int k)//寻找祖先元素
{if(fa[k]==k){return k;}return fa[k]=find(fa[k]);
}
int main()
{scanf("%d%d",&n,&m);//读入for(int i=1;i<=n;i++){fa[i]=i;//初始化}for(int i=1;i<=m;i++)//读入{int x,y,w;scanf("%d%d%d",&x,&y,&w);a[i].x=x;a[i].y=y;a[i].w=w;}sort(a+1,a+1+m,cmp);//排序for(int i=1;i<=m;i++)//枚举边{int x=find(a[i].x);//查询a[i].x的祖先int y=find(a[i].y);//查询a[i].y的祖先if(x==y)//如果已经在一个集合中,则忽略{continue;} fa[x]=y;//合并两个集合sum++;//边数累加ans=max(ans,a[i].w);//更新最大边权值if(sum==n-1)//如果生成树中的边数等于n-1,则最小生成树已经生成{break;//跳出}}printf("%d %d\n",sum,ans);//输出return 0;//完结撒花
}


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

相关文章

最小生成树(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…

数据结构—最小生成树

目录 一、生成树 二、最小生成树&#xff08;代价最小树&#xff09; 三、求最小生成树 1、Prim算法&#xff08;普里姆&#xff09; 2.Kruskal 算法&#xff08;克鲁斯卡尔&#xff09; 3.Prim算法和Kruskal算法对比 一、生成树 连通图的生成树是包含图中全部顶点的一个…

最小生成树详解

目录 最小生成树简介 什么是树 什么是生成树 什么是最小生成树 最小生成树的做法 Kruscal&#xff08;克鲁斯卡尔&#xff09;算法 思路 代码 其他算法 最小生成树简介 什么是树 树&#xff08;tree&#xff09;是一种特殊的图&#xff0c;一个图要成为树要满足三个条…

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

今天做洛谷的时候刷到好多图论的题&#xff0c;发现自己在这一方面算法的掌握还是有待提高啊。在这就先介绍最小生成树的算法吧。 最小生成树 最小生成树&#xff08;minimum spanning tree&#xff09;是由n个顶点&#xff0c;n-1条边&#xff0c;将一个连通图连接起来&…

详解: 最小生成树

详解: 最小生成树算法 最小生成树&#xff08;Minimum Spanning Tree, MST&#xff09;是在一个给定的无向图G(V, E)中求一棵树&#xff0c;使得这棵树有图G的所有顶点&#xff0c;且所有边都来自图G中的边&#xff0c;并且满足整棵树的边权之和最小. 最下生成树满足如下性质…

最小生成树

目录 一、最小生成树定义&#xff1a; 二、普里姆算法&#xff08;Prim&#xff09; 三、Kruskal算法&#xff08;克鲁斯卡尔&#xff09; 小结&#xff1a; 下一篇&#xff1a;最短路径算法 问题&#xff1a; 一、最小生成树定义&#xff1a; 对于一个带权连通无向图G(V,E)&am…

最小生成树——Prim算法(详细图解)

目录 最小生成树的概念 经典题目 prim算法简介 prim算法解析 &#xff08;详细图解&#xff09; 代码实现 代码实战 最小生成树的概念 在一给定的无向图G (V, E) 中&#xff0c;(u, v) 代表连接顶点 u 与顶点 v 的边&#xff0c;而 w(u, v) 代表此的边权重&#xff0c;若…