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算法求图的最小生成树的更多相关文章

  1. 【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数

    题意:求一个图的最小生成树个数. 矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值. 度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是 ...

  2. [Python] 弗洛伊德(Floyd)算法求图的直径并记录路径

    相关概念 对于一个图G=(V, E),求图中两点u, v间最短路径长度,称为图的最短路径问题.最短路径中最长的称为图的直径. 其中,求图中确定的某两点的最短路径算法,称为单源最短路径算法.求图中任意两 ...

  3. 利用Kruskal算法求最小生成树解决聪明的猴子问题 -- 数据结构

    题目:聪明的猴子 链接:https://ac.nowcoder.com/acm/problem/19964 在一个热带雨林中生存着一群猴子,它们以树上的果子为生.昨天下了一场大雨,现在雨过天晴,但整个 ...

  4. 图的普里姆(Prim)算法求最小生成树

    关于图的最小生成树算法------普里姆算法 首先我们先初始化一张图: 设置两个数据结构来分别代表我们需要存储的数据: lowcost[i]:表示以i为终点的边的最小权值,当lowcost[i]=0说 ...

  5. HDU-1233 还是畅通工程 (prim 算法求最小生成树)

    prim 算法求最小生成树 还是畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Oth ...

  6. 图->连通性->最小生成树(克鲁斯卡尔算法)

    文字描述 上一篇博客介绍了最小生成树(普里姆算法),知道了普里姆算法求最小生成树的时间复杂度为n^2, 就是说复杂度与顶点数无关,而与弧的数量没有关系: 而用克鲁斯卡尔(Kruskal)算法求最小生成 ...

  7. 图->连通性->最小生成树(普里姆算法)

    文字描述 用连通网来表示n个城市及n个城市间可能设置的通信线路,其中网的顶点表示城市,边表示两城市之间的线路,赋于边的权值表示相应的代价.对于n个定点的连通网可以建立许多不同的生成树,每一棵生成树都可 ...

  8. 无向带权图的最小生成树算法——Prim及Kruskal算法思路

    边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以 ...

  9. hdu 1233:还是畅通工程(数据结构,图,最小生成树,普里姆(Prim)算法)

    还是畅通工程 Time Limit : 4000/2000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submis ...

随机推荐

  1. ES6语法——let和const

    一.let 1.定义 ES6新增了let命令,用来声明变量,用法类似于var,但是和var有一定的区别 2.let只在块级作用域内有效 首先来看一个比较简单的例子,请告诉我,他们分别输出什么 //代码 ...

  2. 中介者模式(c++实现)

    中介者模式 目录 中介者模式 模式定义 模式动机 UML类图 源码实现 优点 缺点 模式定义 中介者模式(Mediator),用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显示地相互引用, ...

  3. 设计模式:abstract factory模式

    含义:抽象工厂将“抽象零件”组装成“抽象产品” 理解:相比于工厂方法模式,可以根据不同的接口创建不同的产品,说白了就是将一个接口变成两个接口,各自返回不同的抽象产品 例子: class Car //抽 ...

  4. 001.Nginx简介

    一 Nginx概述 1.1 Nginx简介 Nginx是一个高性能的HTTP和反向代理web服务器,Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在B ...

  5. 疑难杂症1-去掉网站里的特殊编码&#65279

    编辑器:Visual Studio 2019项目框架:Net Core 3.1 + AutoFac 特殊符号&#65279,这是编码UTF-8 + BOM的特殊标记,是隐藏的,但是会被浏览器翻 ...

  6. VS Code 上那些沙雕插件

    本文整理自网络,作者不详,如有侵权,则可删除. VS Code 作为前端最牛逼的一个前端编辑器,可以说是最流行的开发工具了,以其可支持扩展程序(通过安装扩展程序,VS Code 可以支持更多新的语言. ...

  7. 阿里云OSS服务器的使用

    关于文件上传,我们一般使用OSS服务器.大致为两种上传方式: 详情官网参考:https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.1 ...

  8. 设备管理的数据库路径是/storage/sdcard0/data/devuce-db

    设备管理的数据库路径是/storage/sdcard0/data/devuce-db 数据库文件名全路径是/storage/sdcard0/data/devuce-db/device.db 数据库文件 ...

  9. PHPSTORM断点调试配置

    一.安装Xdebug xdebug官方提供了一个非常友好的安装指导: https://xdebug.org/wizard.php 打开上面的网站,将你的phpinfo页面输出的内容复制到表单中,然后点 ...

  10. Day05_vue入门

    学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 学习目标 ...