最小生成树——Kruskal算法理解
背景:本文是在小甲鱼数据结构教学视频中的代码的基础上,添加详细注释而完成的。该段代码并不完整,仅摘录了核心算法部分,结合自己的思考,谈谈理解。
Prim算法理解:
如图(摘录自小甲鱼教学视频中的图片),是一个带有权值的连通网:
根据上图可以列写出该连通网的邻接表,为了方便直观的理解:(邻接表初始化需按照权值增序排列)
edges数组 | begin | end | weight |
edge0 | 4 | 7 | 7 |
edge1 | 2 | 8 | 8 |
edge2 | 0 | 1 | 10 |
edge3 | 0 | 5 | 11 |
edge4 | 1 | 8 | 12 |
edge5 | 3 | 7 | 16 |
edge6 | 1 | 6 | 16 |
edge7 | 5 | 6 | 17 |
edge8 | 1 | 2 | 18 |
edge9 | 6 | 7 | 19 |
edge10 | 3 | 4 | 20 |
edge11 | 3 | 8 | 21 |
edge12 | 2 | 3 | 22 |
edge13 | 3 | 6 | 24 |
edge14 | 4 | 5 | 26 |
以下简单描述算法运行的流程(仅描述前几次循环,旨在理解算法工作过程),主要记录和对比parent数组和最小生成树的的逐渐生成的过程:
Kruskal算法核心思想:尽可能只选用权值最小的边连成树,即为最小生成树,因此以权值升序顺序对各边进行循环判断。最理想的情况就是权值最小的几条边恰好连成最小生成树,但是实际过程中很可能会在连接过程中形成环路(树中不允许有环路),因此一个重要的步骤就是判断当前边的加入是否会导致生成树中出现环路(即代码中parent数组的作用和m!=n判断条件的来历)。
Kruskal算法和Prim算法的主要区别就是Prim算法是以定点为单位,Kruskal算法是以边为单位。因此这里所说的(第一次、第二次)循环过程实际是对于上面的邻接表中每一条进行循环判断(是否需要添加到最小生成树中)。
在理解以下过程的时候,先浏览几遍最下方的代码,逐步对比,最容易理解。
以下对于边以及循环次数的命名以0开始,为了和上面的邻接表相对应,以防止混淆。
0、第0次(edge0)
第0次循环,对第0条边进行判断:
edges数组 | begin | end | weight |
edge0 | 4 | 7 | 7 |
执行Find函数,得到的n = 4,m = 7。
m != n 表示不存在环路(这里不理解可以继续看以下的几个循环),则在parent数组中记录这条边带来的连接关系(parent[4] = 7)。
parent数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | n | m |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | \ | \ |
第0次 | 0 | 0 | 0 | 0 | 7 | 0 | 0 | 0 | 0 | 4 | 7 |
生成树:
1、第1次
第1次循环,对第1条边进行判断:
edges数组 | begin | end | weight |
edge1 | 2 | 8 | 8 |
执行Find函数,得到的n = 2,m = 8。
m != n 表示不存在环路,则记录连接关系(parent[2] = 8)。
parent数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | n | m |
第0次 | 0 | 0 | 0 | 0 | 7 | 0 | 0 | 0 | 0 | 4 | 7 |
第1次 | 0 | 0 | 8 | 0 | 7 | 0 | 0 | 0 | 0 | 2 | 8 |
生成树:
此处省略几次循环......只叙述比较有特点的循环。
4、第4次
第4次循环,对第4条边进行判断:
edges数组 | begin | end | weight |
edge4 | 1 | 8 | 12 |
执行Find函数(参考下面第3次迭代后的parent数组),parent[1] = 5; parent[5] = 8; 得到的n = 5。parent[8] = 0; 得到m = 8。
m != n 表示不存在环路,则记录连接关系(parent[5] = 8)。
parent数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | n | m |
第3次 | 1 | 5 | 8 | 0 | 7 | 0 | 0 | 0 | 0 | ||
第4次 | 1 | 5 | 8 | 0 | 7 | 8 | 0 | 0 | 0 | 5 | 8 |
生成树:
这里要注意:parent数组中的对应关系并不表示生成树中的边的关系,比如之前的循环中会在parent数组中添加如下内容:parent[1] = 5; 它表示的是1和5定点在同一个生成树中,之间存在连接关系,但并不表示存在V1->V5这样的一条边。(我自己理解的是,这个关系实际是由V0->V5的这样的一条边的加入而生成的,但是parent[0]已经被幅值为1,即表示与V1存在连接关系,故借用V1来表示出这个关系,自己的一种理解,可能错误,不要干扰思维)。
此处再次省略几次循环......只叙述一次比较特殊的循环(m==n的情况)。
7、第7次
第7次循环,对第7条边进行判断:
edges数组 | begin | end | weight |
edge7 | 5 | 6 | 17 |
执行Find函数(参考下面第6次迭代后的parent数组),parent[5] =85; parent[8] = 6; 得到的n = 6。parent[6] = 0; 得到m = 6。
m == n 表示存在环路,则忽略这条边(不添加到最小生成树中)。
parent数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | n | m |
第6次 | 1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 6 | ||
第7次 | 1 | 5 | 8 | 0 | 7 | 8 | 0 | 0 | 6 | 6 | 6 |
在实际的生成树可以直观的看出V5和V6之间的连线不应该加入(会形成环路),如下图为进行第7次循环之前的生成树情况:
可见,V5->V6边的加入将导致最小生成树中出现环路,因此舍弃。
......
如此对所有边进行循环,判断是否应该加入最小生成树中,直至循环结束,则生成树完成。
代码如下:(仅Kruskal算法的两个核心函数)
int Find(int *parent,int f)
{
/* parent该数组元素>0表示已完成的生成树中存在与该顶点有连接关系的顶点 */
while(parent[f] > 0)
{
/* 则迭代寻找与该点存在连接关系的结束顶点(当前所在树的结束顶点) */
f = parent[f];
}
return f;
}
void MiniSpanTree_Kruskal(MGraph G)
{
int i,n,m;
/* 边数组:应按照边的权值升序进行初始化 */
Edge edges[MAXEDGE];
/* parent数组用来存放顶点之间的连接关系 以判断是否存在环路 */
int parent[MAXVEX];
/* parent数组初始化 */
for(i=0;i<G.numVertexes;i++)
{
parent[i] = 0;
}
for(i=0;i<G.numVertexes;i++)
{
n = Find(parent,edges[i].begin);
m = Find(parent,edges[i].end);
/* 若n == m则表示形成环路 */
if(n != m)
{
/* 若未形成环路 */
/* 将该边添加到生成树中(此处即打印) */
/* 将由该边引起的连接关系保存到parent数组中(注意这里不是简单的将边保存到parent数组中 而是保存了一种连接关系) 表示该顶点已经在生成树中 */
/* 存放方式:parent[p] = q表示:从顶点p到顶点q存在通路(即顶点p和顶点q在同一个生成树中) */
parent[n] = m;
printf("(%d,%d) %d",edges[i].begin,edges[i].end,edges[i].weight);
}
}
}
——cloud over sky
——2020/3/12
最小生成树——Kruskal算法理解的更多相关文章
- 【转】最小生成树——Kruskal算法
[转]最小生成树--Kruskal算法 标签(空格分隔): 算法 本文是转载,原文在最小生成树-Prim算法和Kruskal算法,因为复试的时候只用到Kruskal算法即可,故这里不再涉及Prim算法 ...
- 求最小生成树——Kruskal算法
给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法.这篇文章先介绍Kruskal算法. Kruskal算法的基本思想:先将所有边按权值从小到大排序,然后按顺 ...
- 数据结构:最小生成树--Kruskal算法
Kruskal算法 Kruskal算法 求解最小生成树的还有一种常见算法是Kruskal算法.它比Prim算法更直观.从直观上看,Kruskal算法的做法是:每次都从剩余边中选取权值最小的,当然,这条 ...
- 【一个蒟蒻的挣扎】最小生成树—Kruskal算法
济南集训第五天的东西,这篇可能有点讲不明白提前抱歉(我把笔记忘到别的地方了 最小生成树 概念:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的 ...
- 最小生成树kruskal算法、
克鲁斯卡尔(kruskal) //kruskal算法生成最小生成树. //对边集数组Edge结构的定义 typedef struct { int begin; int end; int weight; ...
- 最小生成树——kruskal算法
kruskal和prim都是解决最小生成树问题,都是选取最小边,但kruskal是通过对所有边按从小到大的顺序排过一次序之后,配合并查集实现的.我们取出一条边,判断如果它的始点和终点属于同一棵树,那么 ...
- 最小生成树Kruskal算法
Kruskal算法就是把图中的所有边权值排序,然后从最小的边权值开始查找,连接图中的点,当该边的权值较小,但是连接在途中后会形成回路时就舍弃该边,寻找下一边,以此类推,假设有n个点,则只需要查找n-1 ...
- 最小生成树------Kruskal算法
Kruskal最小生成树算法的概略描述:1 T=Φ:2 while(T的边少于n-1条) {3 从E中选取一条最小成本的边(v,w):4 从E中删去(v,w):5 if((v,w)在T中不生成环) { ...
- 最小生成树 kruskal算法&prim算法
(先更新到这,后面有时间再补,嘤嘤嘤) 今天给大家简单的讲一下最小生成树的问题吧!(ps:本人目前还比较菜,所以最小生成树最后的结果只能输出最小的权值,不能打印最小生成树的路径) 本Tianc在刚学的 ...
随机推荐
- golang之reflect
reflect,反射. 利用reflect,可以得到一个struct的相关信息. package main import ( "fmt" "reflect" ) ...
- vue-infinite-scroll------vue的无线滚动插件
vue-infinite-scroll------vue的无线滚动插件 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 V ...
- spring mvc从前台往后台传递参数的三种方式
jsp页面: 第一种:使用控制器方法形参的方式(常用) 第二种:使用模型传参的方式(如果前台往后台传递的参数非常多,如果还使用形参的方式传递,非常复杂.我们可以使用模型传参的方式,把多 个请求的参数 ...
- docker-compose安装rabbitmq集群(主从集群---》镜像集群)
docker-compose安装rabbitmq集群(主从集群--->镜像集群) yls 2020/5/11 创建docker-compose.yml 文件 version: '3' servi ...
- 仅需60秒,使用k3sup快速部署高可用K3s集群
作者简介 Dmitriy Akulov,连续创业者,16岁时搭建了开源CDN公共服务jsDelivr的v1版本.目前是边缘托管平台appfleet创始人. 原文链接: https://ma.ttias ...
- 力扣题解-面试题10- II. 青蛙跳台阶问题
题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶.求该青蛙跳上一个 n 级的台阶总共有多少种跳法. 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008, ...
- python datetime 转timestamp
import datetime import time d1=datetime.date.today() t1=time.mktime(d1.timetuple()) d2=datetime.date ...
- centOS 开启服务器后无法访问(大坑啊)
在开启了nodejs后,发现虽然ssh访问到主机,但是公网不能访问. 一番调试发现程序是正常的,也确实在监听着端口.折腾良久无果,在Vultr上发帖求助.几分钟后Vultr团队的工程师Joshua B ...
- 重学 Java 设计模式:实战工厂方法模式
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!
- Redis 消息中间件 ServiceStack.Redis 轻量级
问题: 公司开了个新项目,算上我一共3个人.车间里机台通过流水线连通联动的玩意.一个管理控制系统连接各个机台和硬件.专机类型就有5种,个数差不多20个左右. 软件规划的时候采用总分的结构,管理控制系统 ...