Kruscal算法求图的最小生成树
Kruscal算法求图的最小生成树
概述
和Prim算法求图的最小生成树一样,Kruscal算法求最小生成树也用到了贪心的思想,只不过前者是贪心地选择点,后者是贪心地选择边。而且在算法的实现中,我们还用用到了并查集(也称不相交集的)Union /Find 算法来判断两个节点连通后会不会形成一个环。该算法的思想很简单:将图的所有边按从小到大顺序排序,每次都选取权值最小的边加入最小生成树,如果该边的加入会使生成树形成一个环,则跳过该边。
这里引入并查集的概念,可以使问题变得简单化。并查集就是利用一个数组sets,如果sets[a]=b,那么我们说a和b在一个生成树(集合)中,且a的双亲是b,如果sets[a]=a,那么我们说a是一个生成树的根。一个图的最小生成树在还没有完全生成前,可能存在多个互不连通的生成树,他们是不相交的集合,我们需要把这些不同的生成树连通起来。
于是,通过定义一个findroot函数,我们可以找到某顶点的双亲,然后找到该顶点的双亲的双亲,最终找到顶点所在最小生成树的根,例如:如果我们知道sets[a]=b,sets[b]=c,sets[c]=c,那我们可以说a、b、c在同一棵生成树(集合)里,且所在最小生成树的根为c。假设另一个不相交的生成树的根为d,如果我们令sets[c]=d,则将这两个生成树合并为了一个大的生成树,其根为d。
算法图解
1.创建一个9条边,6个顶点的带权无向连通图。

初始状态的并查集如下:

2.将图所有边按权值进行排序。选择权值最小的一条边的两个邻点。为了避免混乱,统一约定该边右边的邻点加入左边的邻点的并查集中,在图中表示为2顶点挂在1顶点下面。


3.就这样,按照权值从小到大不断遍历所有边,都统一把边的大序号的邻点加入小序号的邻点所在的生成树中,3顶点挂在2顶点下面,4顶点挂在3顶点下面,5顶点挂在4顶点下面,6顶点挂在2顶点的下面,最终最小生成树不断长大,且最后所有顶点本质上都挂在1顶点下面,即最小生成树的根为1顶点。


4.最好还剩4条边没有遍历。但我们发现,这4条边的两个邻点都在同一个生成树中了,即他们findroot的结果都是1。如果我们把任意一条边的两个邻点连接起来,都会形成一个环,这是不允许的。故最小生成树的生成就此结束。
代码
1.引入头文件,和之前所讲的的其他图论算法不同的是,这里单独定义一个表示图的边的类,用u,v记录边的邻接点,w记录边的权值。
#include<iostream>
#include<vector>
#include<algorithm>
//这里同之前我们实现prim算法用的邻接矩阵不同
//我们用一直特殊的储存方式储存图的边
using namespace std;
struct edge{
//边的两个邻点,其中u编号小于v编号
int u;
int v;
//边的权值
int w;
};
2.定义表示图的类
struct Graph{
//储存边的容器
vector<edge> edges;
//定义并查集,记录各顶点的“根”,确定各顶点是否在同一棵树里
vector<int> sets;
//构造函数
Graph(int vertexnum,int edgenum);
//析构函数
~Graph();
//kruascal算法的调用接口
void kruscal();
//寻根函数,后面会详细讲解其用途
int findroot(int vertex);
//按权值给图的边排序的函数
void sortedges();
};
3.这里自定义一个排序比较函数,后面给储存边的容器排序时会用到。
//排序自定义操作函数
bool cmp(edge a,edge b){
return a.w<=b.w;
}
4.Graph类的构造函数以及析构函数
Graph::Graph(int vertexnum,int edgenum){
edges.resize(edgenum+1);
sets.resize(vertexnum+1);
//初始化并查集。每个节点相互独立,自己是自己所在生成树的根
for(int i=1;i<=vertexnum;i++){
sets[i]=i;
}
cout<<"请依次输入各边的两个顶点及其权值"<<endl;
//注意,为了跟后面寻根函数配合,要保证u定点编号小于v顶点编号
for(int i=1;i<=edgenum;i++){
int a,b,w;
cin>>a>>b>>w;
if(a>b){
edges[i].u=a;
edges[i].v=b;
}
else{
edges[i].v=a;
edges[i].u=b;
}
edges[i].w=w;
}
}
Graph:: ~Graph(){
edges.clear();
sets.clear();
}
5.Kruscal算法的实现。
//kruascal算法的实现方法
void Graph::kruscal(){
//调用成员函数对图的所有边进行排序
sortedges();
int sum=0;
for(int i=1;i<=edges.size()-1;i++){
/*如果该边的两个顶点的根不相同,则让u邻接点成为v邻接点的根的根。
说形象一点,就是让v邻接点的BOSS归顺于u邻接点的BOSS;
从而让v归顺于u的BOSS,
并将该边的权值加入总和中 */
if(findroot(edges[i].u)!=findroot(edges[i].v)){
sets[findroot(edges[i].v)]=findroot(edges[i].u);
//此处非常容易错,读者写代码时一定要仔细
sum += edges[i].w;
}
//否则不进行任何操作
}
cout<<"最小生成树的权值和为:"<<sum<<endl;
}
6.寻根函数的实现
//利用并查集性质,逐步回溯,找到一个顶点的“根”
int Graph::findroot(int vertex){
while(sets[vertex]!=vertex){
vertex=sets[vertex];
}
return vertex;
}
7.对顶点按照权值排序函数的实现
//按权值对顶点进行从小到大排序的函数
void Graph::sortedges(){
sort(edges.begin(),edges.end(),cmp);
}
8.测试部分。定义一个6个顶点9条边的图,调用接口求该图的最小生成树
int main()
{
//实例化一个6个顶点,9条边的图对象
Graph* G=new Graph(6,9);
//调用接口实现算法
G->kruscal();
system("pause");
return 0;
}
输出
控制台输出结果:

Kruscal算法求图的最小生成树的更多相关文章
- 【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数
题意:求一个图的最小生成树个数. 矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值. 度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是 ...
- [Python] 弗洛伊德(Floyd)算法求图的直径并记录路径
相关概念 对于一个图G=(V, E),求图中两点u, v间最短路径长度,称为图的最短路径问题.最短路径中最长的称为图的直径. 其中,求图中确定的某两点的最短路径算法,称为单源最短路径算法.求图中任意两 ...
- 利用Kruskal算法求最小生成树解决聪明的猴子问题 -- 数据结构
题目:聪明的猴子 链接:https://ac.nowcoder.com/acm/problem/19964 在一个热带雨林中生存着一群猴子,它们以树上的果子为生.昨天下了一场大雨,现在雨过天晴,但整个 ...
- 图的普里姆(Prim)算法求最小生成树
关于图的最小生成树算法------普里姆算法 首先我们先初始化一张图: 设置两个数据结构来分别代表我们需要存储的数据: lowcost[i]:表示以i为终点的边的最小权值,当lowcost[i]=0说 ...
- HDU-1233 还是畅通工程 (prim 算法求最小生成树)
prim 算法求最小生成树 还是畅通工程 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Oth ...
- 图->连通性->最小生成树(克鲁斯卡尔算法)
文字描述 上一篇博客介绍了最小生成树(普里姆算法),知道了普里姆算法求最小生成树的时间复杂度为n^2, 就是说复杂度与顶点数无关,而与弧的数量没有关系: 而用克鲁斯卡尔(Kruskal)算法求最小生成 ...
- 图->连通性->最小生成树(普里姆算法)
文字描述 用连通网来表示n个城市及n个城市间可能设置的通信线路,其中网的顶点表示城市,边表示两城市之间的线路,赋于边的权值表示相应的代价.对于n个定点的连通网可以建立许多不同的生成树,每一棵生成树都可 ...
- 无向带权图的最小生成树算法——Prim及Kruskal算法思路
边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...
- hdu 1233:还是畅通工程(数据结构,图,最小生成树,普里姆(Prim)算法)
还是畅通工程 Time Limit : 4000/2000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other) Total Submis ...
随机推荐
- 集训 T4-分配时间
题目: 思路: 这个题目正解为dp,但是我并不会dp,所以写了个类似于T3的搜索.(然后就70分了 先看一张图: 我的思路是把写名字的时间和写卷子的时间算在了一起(下标表示时间点,比如下标2那一行代表 ...
- faker生成器生成虚拟数据的Python模块
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:行哥 今天给大家介绍一个Faker模块,一款基于Python的测试数 ...
- org.apache.commons.net.ftp.FTPConnectionClosedException: Connection closed without indication
Ftp问题 最近遇到了ftp读取中文乱码的问题,代码中使用的是FtpClient.google一下找到了解决方案. FTP协议里面,规定文件名编码为iso-8859-1,FTP类中默认的编码也是这个. ...
- .log文件超过2.56MB?Pycharm的.log文件读取不完全?.log文件无法被调用?
问题截图: 问题表现情况: 1.pycharm头部出现上图警告 2.该.log文件读取不完全 3.该.log文件无法被调用 解决步骤: 参考博客:https://blog.csdn.net/Shen1 ...
- Python灰帽子:黑客与逆向工程师的Python编程之道|百度网盘免费下载|新手黑客入门
百度网盘免费下载:Python灰帽子:黑客与逆向工程师的Python编程之道 提取码:tgpg 目录 · · · · · · 第1章 搭建开发环境 11.1 操作系统要求 11.2 获取和安装Pyt ...
- sqlite 显示表内容时乱码,无法正常显示汉字,
把txt文件另存为时,选择编码为utf-8即可
- 手写Vuex源码
Vuex原理解析 Vuex是基于Vue的响应式原理基础,所以无法拿出来单独使用,必须在Vue的基础之上使用. 1.Vuex使用相关解析 main.js import store form './s ...
- 第二章 Java基础知识(上)
2.1.注释 单行注释 // 注释内容 多行注释 /* 注释内容 */ 文档注释 /**注释内容 */ 2.2.关键字 定义:在Java语言中被赋予特殊含义的小写单词 分类: 2.3.标识符 定义:标 ...
- Day01_搭建环境&CMS服务端开发
学成在线 第1天 讲义-项目概述 CMS接口开发 1 项目的功能构架 1.1 项目背景 受互联网+概念的催化,当今中国在线教育市场的发展可谓是百花齐放.如火如荼. 按照市场领域细分为:学前教育.K12 ...
- PHP xml_set_element_handler() 函数
定义和用法 xml_set_element_handler() 函数规定在 XML 文档中元素的起始和终止调用的函数. 如果成功,该函数则返回 TRUE.如果失败,则返回 FALSE.高佣联盟 www ...