详解最小生成树中的克鲁斯卡尔算法

0x01.关于克鲁斯卡尔算法

Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表。克鲁斯卡尔算法主要针对边集数组展开。

0x02.基础代码

这个算法主要是针对边集数组,先来看一下边集数组的结构:

//边集数组
typedef struct
{int begin;int end;int weight;
}Edge;

通常用到邻接矩阵,所以还需要由邻接矩阵转化为边集数组。另外这个算法还需要按照边的权值升序排序。

void OperationEdge(Graph G, Edge* edges)
{int i, j,k;k = 0;for (i = 0; i < G.numv; i++){for (j = i+1; j < G.numv; j++)//只需要转化邻接矩阵的一半,无向图{if (G.edge[i][j] != INTMAX && G.edge[i][j] != 0){edges[k].begin = i;edges[k].end = j;edges[k].weight = G.edge[i][j];k++;}}}for (i = 0; i < k-1; i++)//简单交换排序{for (j = i + 1; j < k; j++){if (edges[i].weight > edges[j].weight){Edge tmp;tmp = edges[i];edges[i] = edges[j];edges[j] = tmp;}}}
}

克鲁斯卡尔算法代码(简单版):

void MinSpanTree_Kruskal(Graph G)
{int i, n, m;Edge edges[MAXSIZE];//定义边集数组int parent[MAXSIZE];//用于判断树是否会成环OperationEdge(G, edges);//邻接矩阵向边集数组转换,并升序排序for (i = 0; i < G.numv; i++)//初始化{parent[i] = 0;}int cou = 0;//计数器,记录实际上已加入生成树的边数for (i = 0; i < G.nume; i++){if (cou == G.numv - 1)//加入生成树的边等于顶点数-1,已经完成了最小生成树的建立,退出循环{printf("最小生成树构造完成!!!");break;}n = Find(parent, edges[i].begin);m = Find(parent, edges[i].end);if (n != m)//没有成环,加入生成树{cou++;parent[n] = m;printf("边 %c到%c ,权值为 %d ,已加入生成树", G.ver[edges[i].begin], G.ver[edges[i].end], edges[i].weight);}}
}

0x03.过程详解

首先看一个最原始的图:

再来看一下边集数组中的值:

开始模拟运行:

第一次循环,由于parent数组内容都为0,所以进入Find函数后之间返回,n=4,m=7,此时n于m不相等。parent[4]=7,边E--H加入生成树。(具体parent如何发挥作用看下文)

第二次循环,0进入Find函数后之间返回,n=0,1也立即返回,m=1,此时parent[0]=1,边A--B加入生成树。

第三次循环,0进入Find函数后,由于Find[0]=1,所以开始再一次小循环,f=parent[0]=1,parent[1]=0,退出小循环,所以返回1,n=1,5进入Find函数后,立即返回,m=5,parent[1]=5,边A--F加入生成树。

第四次循环,1进入Find函数后,parent[1]=5,所以继续进行小循环,f=parent[1]=5,parent[5]=0,退出循环,n=5,8进入循环后,立即退出,m=8,parent[5]=8,边B--G加入生成树。

此时的parent为[1,5,0,0,7,8,0,0,0].

第五次循环,3进入Find函数,parent[3]=0,立即返回,n=3,7进入Find函数,立即返回,m=7,parent[3]=7,边D--H加入生成树。

此时的parent为[1,5,0,7,7,8,0,0,0].

第六次循环,1进入Find函数,parent[1]=5,继续,f=parent[1]=5,parent[5]=8,f=parent[5]=8,parent[8]=0,返回,n=8,6进入Find函数,parent[6]=0,返回,m=6,parent[8]=6,边B--G加入生成树。

此时的parent为[1,5,0,7,7,8,0,0,6].

第七次循环,5进入Find函数,parent[5]=8,f=parent[5]=8,parent[8]=6,f=parent[8]=6,parent[6]=0,返回,n=6,6进入函数,parent[6]=0,返回,m=6,此时m=n,成环,由图可以看出的确成环,具体原理在下方。

第八次循环,1进入Find函数,parent[1]=5,f=parengt[1]=5,parent[5]=8,f=parent[5]=8,parent[8]=6,f=parent[6]=0,返回,n=6,2进入函数,parent[2]=0,返回,m=2,不成环,parent[6]=2,边B--C加入生成树。

此时的parent为[1,5,0,7,7,8,2,0,6].

第九次循环,6进入Find函数,parent[6]=2,f=parent[6]=2,parent[2]=0,返回,n=2,7进入循环,parent[7]=0,返回,m=7,不成环,parent[2]=7,边G--H加入生成树。

由于9个顶点只需要八条边就能完成生成树,所以到此,生成树其实已经完成。我们可以试一试,此时,继续进入parent函数会发生什么。

第十次循环,3进入parent函数,parent[3]=7,f=parent[3]=7,parent[7]=0,返回,n=7,4进入循环,parent[4]=7,parent[7]=0,m=7,我们发现,当parent数组中只有一个值为0的情况下,任意两个数传进Find函数,一定都是等于那个值为0的下标,后序所有的循环无意义。

0x04.原理详解

parent数组和Find函数究竟如何理解?

parent数组实际上是并查集这种数据结构的一种表达,关于并查集可以百度了解。

在这个算法的不断加入生成树中,其实形成了许多的小树,而整个算法的目的就是将这些小树连接成一棵树,Find函数的目的就是找到每棵小树的根结点,也就是说返回的n和m的值其实是两棵小树的根节点,如果相等,那么就有两棵小树的根结点相同,既然是两棵小树,根节点相同了,肯定就是连通了,如果不相等,就连通两棵小树,连通的代码是parent[n]=m,这端代码的实际含义就是连通了两棵小树,并以新加入的结点作为的根结点,其中Find函数的那个小循环其实就是寻找一颗小树的根结点,如果和其它没有连通,那么这条边单独做为一棵小树,并以一头为根,f=parent[f],这段代码其实就是不断向上去找的含义,去找到根结点。

原理如上了,那么我们怎么去具体理解这些代码呢?

对于parent数组,我们可以看看什么时候往里加了元素,加的元素的下标和值都是什么意义,不难发现,parent数组一直只是往里添加并查看元素,并没有改变过里面元素的内容,添加元素的关键代码就是parent[n]=m,也就是说在n的位置上添加数值m,我们从刚开始的时候去看,刚开始parent里面都为0,进入Find函数都是直接返回,如第一条边,4,7,直接返回后,有parent[4]=7,这个就是说,下标为4的顶点的根设置为7,我们再来看一下第二个循环,0,1,都是立即返回,parent[0]=1,说明下标为0的结点的根为1,第三次,0,5,进入Find函数后,返回n=1,也就是说定的顶点A的根是1,也就是B,m=5,然后有parent[1]=5,说明顶点B的根结点变成了5,就这样一次理解,我们就可以知道parent数组里面的内容含义是什么。

对于Find函数,我们可以看一下参数和返回分别是什么样的情况,参数是f,也就是边集数组里的边的一个端点,返回条件是,parent数组中的f位置的值为0,其实也就是找到根结点了,f=parent[f],就是不断的向上去找根结点,可以理解为一个顶点的根节点找到后,再继续找这个根结点的根节点,最后找到了这棵树的根结点。

0x05.克鲁斯卡尔算法感悟

这个算法巧妙的利用了一个已经排序的边集数组,然后从头开始去把里面的边加入生成树,应为是有序的,所以从一开始,就已经满足了最小的这个条件,接下来只要判断这些边能不能生成树,也就是由没有闭合就行了,代码简便不少,非常精炼。

0x06.普里姆算法和克鲁斯卡尔算法比较

普里姆主要针对顶点展开,而克鲁斯卡尔主要针对边,各有优势,前者适合稠密图,后者适合稀疏图。

本章结束。

【数据结构基础整理】图--06:克鲁斯卡尔算法详解相关推荐

  1. java克鲁斯卡尔算法_Java语言基于无向有权图实现克鲁斯卡尔算法代码示例

    所谓有权图,就是图中的每一条边上都会有相应的一个或一组值.通常情况下,这个值只是一个数字 如:在交通运输网中,边上的权值可能表示的是路程,也可能表示的是运输费用(显然二者都是数字).不过,边上的权值也 ...

  2. 克鲁斯卡尔算法c语言,Kruskal算法(一)之 C语言详解

    最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总和 ...

  3. c语言克鲁斯卡尔算法

    克鲁斯卡尔思路以及代码分析 我们用邻接矩阵来表示该图,克鲁斯卡尔算法思想及找到最小边,查看是否形成回路,若形成回路则这条边不形成,若不形成回路则构成最小路径,以此类推.(思路和代码都在下方) 第一步e ...

  4. SF图像滤镜/美颜/美妆算法详解与实战

    本专栏将结合本热多年相关经验,从传统算法到火热的AI算法,给大家详细讲解目前在PC图像软件.手机图像处理类应用app,以及视频直播等应用类型中,图像视频的滤镜特效,人像美颜美妆特效的算法理论,并结合具 ...

  5. 【数据结构】图的应用(普利姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法、拓扑排序)

    最小生成树 什么是最小生成树 是一棵树 - 无回路 - |V|个顶点一定有|V|-1条边 是生成树 - 包含全部顶点 - |V|-1条边全在图里 贪心算法 什么是"贪":每一步都要 ...

  6. 克鲁斯卡尔算法(Kruskal Algorithm)——图的最小生成树

    听骚话讲作者 [手动滑稽] 其实早就该写这篇博客啦. 图的最小生成树 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边.最小生成树可以 ...

  7. 大话数据结构-普里姆算法(Prim)和克鲁斯卡尔算法(Kruskal)

    5 最小生成树   构造连通网的最小代价生成树称为最小生成树,即Minimum Cost Spanning Tree,最小生成树通常是基于无向网/有向网构造的.   找连通网的最小生成树,经典的有两种 ...

  8. 数据结构与算法|最小生成树算法(普里姆算法、克鲁斯卡尔算法)

    最小生成树算法 C语言代码部分来自小甲鱼的<数据结构与算法> 文章目录 最小生成树算法 一.普里姆(Prim)算法 1.C语言代码 2.算法思路 二.克鲁斯卡尔(Kruskal)算法 1. ...

  9. 最小生成树------克鲁斯卡尔算法(数据结构)

    树(Tree):如果一个无向连通图中不存在回路,则这种图称为树. 生成树 (Spanning Tree):无向连通图G的一个子图如果是一颗包含G的所有顶点的树,则该子图称为G的生成树. 生成树是连通图 ...

最新文章

  1. 参加java培训后,就业方向有哪些
  2. iOS逆向工程的一些资料
  3. python 网页自动处理_推荐一款 10 行 Python 代码实现网页自动化工具
  4. 使用C#创建SQLite控制台应用程序
  5. oracle网络公开课《存储技术》课件和视频共享下载
  6. 这样合并Python字典,可以让程序的运行效率提高4倍
  7. GridView实现自动编号
  8. x86异常处理与中断机制(2)中断向量表
  9. FileOutputSteam入门
  10. 两种可以支持跨域的方式 - 讲解篇
  11. html5在zigbee中起什么作用,ZigBee是什么?ZigBee在智能家居中扮演什么角色
  12. WINDOWS上svn服务器自动部署
  13. jupyter notebook代码无法运行
  14. php实现hmac sha1,PHP利用HMAC-SHA1签名的实现方法
  15. 鸿蒙磅礴不可以涯际夕 四,一尺树根不出土,凿臼舂米于道左兮,行人为叹生涯悭。...
  16. python打开文件切片_收藏 | 从Python安装到语法基础,小白都能懂的爬虫教程!(附代码)...
  17. 右键新建缺少word、excel选项问题处理
  18. STVD生成HEX格式文件
  19. 今日小程序推荐:汇率即时查-打通微信直接搜一搜
  20. linux 查询日志 去重,linux命令(12)uniq去重

热门文章

  1. 5454. 统计全 1 子矩形(Leetcode 196周赛)
  2. 硬盘开启NCQ功能全解
  3. 计算机的基础操作知识试题,[计算机基本操作知识]计算机操作系统基础知识试题...
  4. AWS VPC 以及VPC的原理
  5. Direct2D学习教程(一)
  6. 拖延症患者的自我反省
  7. 如何计算电商网站页面单跳转化率?
  8. 时间序列模型 (二):移动平均法
  9. 公众号被 SRS 大佬推荐是怎么样一种体验~~
  10. Android基础:签名文件MANIFEST.MF的内容是怎么计算来的?