博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
图的应用——最小生成树——Kruskal算法
阅读量:3947 次
发布时间:2019-05-24

本文共 3036 字,大约阅读时间需要 10 分钟。

最小生成树

一个连通图的生成树是一个极小的连通子图,它含有图中全部顶点,但只有n - 1条边,我们把构造连通图的最小代价生成树称为最小生成树。

在这里插入图片描述
如上左图所示,这是一个无向带权图,而右图是它的生成树之一。显然,生成树不唯一,可能有多颗。
在这里插入图片描述
此图才是上左图的最小生成树。

构造最小生成树有多种算法,经典的有两种,Prim算法和Kruskal算法,此篇博客将围绕Kruskal算法展开。

Kruskal算法

Prim 和Kruskal 算法都属于贪心算法,而Kruskal算法的贪心准则是从剩下的边中选择权值最小且不会产生环路的边加入生成树的边集

基本思想:先构造一个只含 n 个顶点的子图 G,然后从权值最小的边开始,若它的添加不使 G 中产生回路,则在 G 中加入此边,然后按照权值递增的次序依次按规则添加,直至加完 n - 1条边。故,Kruskal算法又称“加边法”。

步骤:

  1. 由于Kruskal算法要求按权值由小到大依次添加,所以我们首先要按权值大小排序。
  2. 排完序后便要准备开始“加边”了,但“加边”之前要先判断,要加入的这条边是否会使子图形成环。
  3. 正式“加边”,合并连通分量。

由于要按权值排序,还要判断是否形成环,所以这里我们定义一个边集数组。

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

那么上图最后转换后的边集数组为:

begin end weight
Edge[0] 0 2 1
Edge[1] 3 5 2
Edge[2] 1 4 3
Edge[3] 2 5 4
Edge[4] 0 3 5
Edge[5] 1 2 5
Edge[6] 2 3 5
Edge[7] 0 1 6
Edge[8] 2 4 6
Edge[9] 4 5 6

以上图为例,我们来看一下它的最小生成树的生成过程:

在这里插入图片描述在这里插入图片描述按照步骤,第五条边应该是(A,D),但由于A,D已在同一集合中,若加边会形成回路,所以(A,D)不加入。

代码

#include 
#include
#include
#define MAXVEX 100 //最大顶点数#define INF 65535 //极大typedef struct {
int vexs[MAXVEX]; //顶点表 int arc[MAXVEX][MAXVEX]; //邻接矩阵 int vex_num, edge_num; //顶点数,边数}MGraph;//边集数组typedef struct{
int begin; int end; int weight;}Edge;bool cmp(const Edge& a, const Edge& b){
return a.weight < b.weight;}//无向图void CreateMGraph(MGraph *G){
int i, j, k ,w; printf("输入顶点数和边数:"); scanf("%d %d", &G->vex_num, &G->edge_num); //建立顶点表 printf("输入顶点:"); for(i = 0; i < G->vex_num; ++i) scanf("%d", &G->vexs[i]); //建立邻接矩阵 for(i = 0; i < G->vex_num; ++i) for(j = 0; j < G->vex_num; ++j) G->arc[i][j] = INF; for(k = 0; k < G->edge_num; ++k) {
printf("输入边的下标(i , j)及权值:"); scanf("%d %d %d", &i, &j, &w); G->arc[i][j] = G->arc[j][i] = w; //矩阵对称 }}//查找连线顶点的尾部(该顶点所在连通子图的尾部)下标int Find(int *parent, int f){
while(parent[f] > 0) f = parent[f]; return f;}void MiniSpanTree_Kruskal(MGraph G){
int i, j, k, n, m; Edge edges[G.edge_num]; //定义边集数组 int parent[G.vex_num]; //判断是否能形成回路 //将邻接矩阵转化为边集数组并按权值大小排序 k = 0; for(i = 0; i < G.vex_num; ++i) {
for(j = i + 1; j < G.vex_num; ++j) //只处理上三角形 {
if(G.arc[i][j] != INF) {
edges[k].begin = i; edges[k].end = j; edges[k].weight = G.arc[i][j]; ++k; } } } std::sort(edges, edges + G.edge_num, cmp); for(i = 0; i < G.vex_num; ++i) parent[i] = 0; //循环判断每一条边 printf("最小生成树:\n"); for(i = 0; i < G.edge_num; ++i) {
n = Find(parent, edges[i].begin); m = Find(parent, edges[i].end); if(n != m) //没有形成回路 {
parent[n] = m; //将此边的结尾顶点加入,表示此顶点已在生成树集合中 printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight); } }}int main(){
MGraph G; CreateMGraph(&G); MiniSpanTree_Kruskal(G); return 0;}

Kruskal算法的时间代价主要依赖边数,所以对于稀疏图有很大的优势。

参考

《数据结构与算法》——王曙燕

《大话数据结构》

转载地址:http://rjqwi.baihongyu.com/

你可能感兴趣的文章
Android 系列 1.3了解Android版本
查看>>
Android 系列 6.28使用正确的复数格式化
查看>>
Android 系列 6.29创建在两个活动之间显示的加载屏幕
查看>>
Android的Gradle技巧 1.2配置SDK版本和其他默认值
查看>>
Android的Gradle技巧 1.3从命令行执行Gradle构建
查看>>
Android的Gradle技巧 1.4从Android Studio执行Gradle构建
查看>>
Android的Gradle技巧 1.5添加Java库依赖关系
查看>>
Android的Gradle技巧 1.6使用Android Studio添加库依赖关系
查看>>
Android的Gradle技巧 1.7配置存储库
查看>>
android Collections 排序,
查看>>
Android的Gradle技巧 2.1设置项目属性
查看>>
Android的Gradle技巧 2.2将应用程序从Eclipse ADT移植到Android Studio
查看>>
Android的Gradle技巧 2.3从Eclipse移植应用程序ADT使用Eclipse
查看>>
昂山素季 Aung San Suu Kyi
查看>>
AI 人工智能第一课 从贝叶斯定理开始
查看>>
朴素贝叶斯python实现
查看>>
Logistic回归原理及公式推导
查看>>
并发性与并行性 并发性与并行性
查看>>
惰性求值,可组合和模块化的JavaScript
查看>>
How to Extend Django User Model 如何扩展Django用户模型
查看>>