最小生成树——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在刚学的 ...
随机推荐
- Python监控文件夹 && 发送邮件
直接上代码: # python3 # -*- coding: utf-8 -*- # 2017/06/16 by luohan from email.mime.text import MIMEText ...
- Java——Java自动装箱和拆箱
一.什么是自动装箱和拆箱: 我们知道java为8种基本类型分别提供了对应的包装类型,在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i=new I ...
- Java λ表达式
目录 Java Lambda表达式 1. 函数式编程思想 2. 体验Lambda的更优写法 3. Lambda表达式的标准格式 4. 练习:使用Lambda表达式(无参无返回) 5. 练习:使用Lam ...
- salesforce零基础学习(九十七)Event / Task 针对WhoId的浅谈
我们在Sales Cloud中经常会创建顾客,如果针对TO C业务,会启用个人顾客,比如针对车企行业,有一些场景是需要卖给个人的,而不只是企业采购.当通过打电话或者其他的场景有潜在客户并且转换成客户以 ...
- 深入理解CSS定位
CSS中有3种定位机制:普通流,浮动和绝对定位.除非专门指定,否则所有框都在普通流中定位.顾名思义,普通流中元素框的位置由HTML元素的位置决定.块级框一个接一个地垂直排列,框之间的垂直距离由框的垂直 ...
- Angular 从入坑到挖坑 - Router 路由使用入门指北
一.Overview Angular 入坑记录的笔记第五篇,因为一直在加班的缘故拖了有一个多月,主要是介绍在 Angular 中如何配置路由,完成重定向以及参数传递.至于路由守卫.路由懒加载等&quo ...
- 【Hadoop离线基础总结】MapReduce倒排索引建立
MapReduce倒排索引建立 求某些单词在文章中出现多少次 有三个文档的内容,求hello,tom,jerry三个单词在其中各出现多少次 hello tom hello jerry hello to ...
- Android 8.1 关机充电动画(三)Android模式
system:Android 8.1 platform:RK3326/PX30 uboot kernel system/core/healthd Android 8.1 关机充电动画(一)模式选择 A ...
- Mysql 常用函数(20)- ceiling 函数
Mysql常用函数的汇总,可看下面系列文章 https://www.cnblogs.com/poloyy/category/1765164.html ceiling 的作用 向上取整,ceil 函数一 ...
- 反射,获取类的属性以及get方法
vo实体类: public class Result { /*** * 返回结果code * ok:10000 * error:20000 */ private String code; /*** * ...