最小生成树——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在刚学的 ...
随机推荐
- 常用设计模式的实现,以及Netty中的设计模式
1.观察者模式 有两个角色,观察者和被观察者.当被观察者发出消息后,注册了的观察者会收到其消息,而没有注册的观察者就不会收到. //定义观察者接口 interface Observer{ //通知观察 ...
- jQuery的事件绑定与触发 - 学习笔记
jQuery的事件绑定与触发 事件绑定 自动触发事件 常用的鼠标事件 事件冒泡和默认行为 事件冒泡 默认行为 获得当前鼠标的位置和按键 jQuery的事件绑定与触发 事件绑定 基本绑定 $(eleme ...
- mysql中的读锁和写锁
当mysql为一个表开启读锁的时候,其他进程包括进程本身没有权利去修改这张表的内容,也没有权利更新,但是可以读取表里面的内容 如下例子 给表上读锁 接着更新一下,显示被锁了 释放锁并插入数据 写锁 查 ...
- js基石之---es7的decorator修饰器
es7的decorator修饰器 装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法. decorator就是给类添加或修改类的变量与方法的. 装饰器是一种函数, ...
- Linux设备模型之kobject
阿辉原创,转载请注明出处 参考文档:LDD3-ch14.内核文档Documentation/kobject.txt,本文中使用到的代码均摘自Linux-3.4.75 ----------------- ...
- 一篇博客带你轻松应对Springboot面试
1. SpringBoot简介 SpringBoot是简化Spring应用开发的一个框架.他整合了Spring的技术栈,提供各种标准化的默认配置.使得我们可以快速开发Spring项目,免掉xml配置的 ...
- quartus II Warning 好的时序是设计出来的,不是约束出来的
一.Warning (15714): Some pins have incomplete I/O assignments. Refer to the I/O Assignment Warnings r ...
- equals方法和 == 的使用
equals方法的使用 equals方法 源码展示: public boolean equals(Object obj) { return (this == obj); } 解析: equals方法默 ...
- Spring全家桶之springMVC(四)
路径变量PathVariable PathVariable Controller除了可以接收表单提交的数据之外,还可以获取url中携带的变量,即路径变量,此时需要使用@PathVariable ...
- Windows 系统如何安装 Docker
1 docker 是基于 unix 开发的系列工具,所以在 windows 上安装 docker 非常容易出现环境不兼容的问题. 如果 windows 版本是 pro,一般是可以直接安装 docker ...