文字描述

  上一篇博客介绍了最小生成树(普里姆算法),知道了普里姆算法求最小生成树的时间复杂度为n^2, 就是说复杂度与顶点数无关,而与弧的数量没有关系;

  而用克鲁斯卡尔(Kruskal)算法求最小生成树则恰恰相反。它的时间复杂度为eloge (e为网中边的数目),因此它相对于普里姆算法而言,适合于求边稀疏的网的最小生成树。

  克鲁斯卡尔算法求最小生成树的步骤为:假设连通网N={V,{E}}, 则令最小生成树的初始状态为只有n个顶点而无边的非连通图 T=(V, {}}, 图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量中,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直至T中所有顶点都在同一连通分量上为止。

示意图:

算法分析:

  实现克鲁斯卡尔的话,可以借助于之前介绍的"堆"(堆排序)和树的等价划分的方法。用”堆”来存放网中的边,则每次选择最小代价的边仅需loge的时间(第一次需e)。又生成树T的每个连通分量可看成一个等价类,则构造T加入新的边的过程类似于求等价类的过程,由此可用MFSet类型来描述顶点,用堆Heap存放弧结点信息,使构造T的过程仅需eloge的时间,由此,克鲁斯

代码实现

 //
// Created by lady on 18-12-15.
// #include <stdio.h>
#include <stdlib.h>
#include <string.h> #define MAX_NODE_NUM 20
#define MAX_ARCH_NUM 30 #define EQ(a, b) ((a)==(b))
#define LT(a, b) ((a) <(b))
#define LQ(a, b) ((a)<=(b)) typedef struct PTNode{
char data[];
int index;
int parent;
}PTNode; typedef struct{
PTNode node[MAX_NODE_NUM+];
int n;
}MFSet; typedef struct ArcBox{
int vex1, vex2;
int weight;
}ArcBox; typedef struct{
ArcBox r[MAX_ARCH_NUM+];
int len;
}HeapType; /* param1 S: S是已存在的集合
* param2 index: index是S中某个子集的成员的下标值
* result: 查找函数,确定S中位置为index所属子集Si,并返回其子集中的根结点的位置。
*/
static int findMFSet(MFSet *S, int index)
{
if(index < || index > S->n)
return -;
int j = ;
for(j=index; S->node[j].parent>; j=S->node[j].parent);
return j;
} /*
* 集合S中位置为index_i和index_j所在的子集互不相交。
* result: 将置为index_i和index_j所在的互不相交的子集合并为一个子集。
*/
static int mergeMFSet(MFSet *S, int index_i, int index_j)
{
int loc_i = -, loc_j = -;
if((loc_i=findMFSet(S, index_i)) < ){
return -;
}
if((loc_j=findMFSet(S, index_j)) < ){
return -;
}
if(loc_i == loc_j){
return -;
}
//结点数少的子集指向结点数大的子集
if(S->node[loc_i].parent > S->node[loc_j].parent){
S->node[loc_j].parent += S->node[loc_i].parent;
S->node[loc_i].parent = loc_j;
}else{
S->node[loc_i].parent += S->node[loc_j].parent;
S->node[loc_j].parent = loc_i;
}
return ;
} static int initialMFSet(MFSet *S, int n)
{
int i = ;
S->n = n;
for(i=; i<=S->n; i++)
{
printf("输入第%d个子集:", i);
scanf("%s", S->node[i].data);
S->node[i].parent = -;
S->node[i].index = i;
}
return ;
} /*
* 返回结点中数据等于data的下标值
*/
static int getLocaofVex(MFSet *S, char data[])
{
int i = ;
for(i=; i<=S->n; i++){
if(!strncasecmp(S->node[i].data, data, sizeof(S->node[].data))){
return S->node[i].index;
}
}
return -;
} static void printMFSet(MFSet *S)
{
printf("打印MFSet结构中的数据:\n");
int i = ;
for(i=; i<=S->n; i++){
printf("index %d:(data %s, parent %d)\n", S->node[i].index, S->node[i].data, S->node[i].parent);
}
printf("\n");
} ////////////////////////////////////////////// static int printHeap(HeapType *H)
{
printf("打印堆结构中的数据:\n");
int i = ;
for(i=; i<=H->len; i++){
printf("index %d: arch:(%d,%d),weight %d)\n", i, H->r[i].vex1, H->r[i].vex2, H->r[i].weight);
}
return ;
} static int initialHeap(HeapType *H, MFSet *S, int n)
{
H->len = n;
int i = ;
char s1[] = {};
char s2[] = {};
char s3[] = {};
int weight = ; char tmp[] = {};
for(i=; i<=H->len; i++)
{
printf("输入第%d条弧信息(顶点1 顶点2 权值):", i);
memset(tmp, , sizeof(tmp));
scanf("%s", tmp);
sscanf(tmp, "%[^','],%[^','],%s[^\\n]", s1, s2, s3);
H->r[i].vex1 = getLocaofVex(S, s1);
H->r[i].vex2 = getLocaofVex(S, s2);
H->r[i].weight = atoi(s3);
}
return ;
} /*
*已知H->r[s,...,m]中记录的关键字除H->r[s].key之外均满足的定义
*本函数调整H-r[s]的关键字,使H->r[s,...,m]成为一个小顶堆(对其中
*记录的弧的权值而言)
*/
void HeapAdjust(HeapType *H, int s, int m)
{
ArcBox rc = H->r[s];
int j = ;
//沿weight较小的孩子结点向下筛选
for(j=*s; j<=m; j*=){
//j为weight较小的孩子结点下标
if(j<m && (!LQ(H->r[j].weight, H->r[j+].weight)))
j+=;
if(LQ(rc.weight, H->r[j].weight))
break;
H->r[s] = H->r[j];
s=j;
}
H->r[s] = rc;
} void HeapSort(HeapType *H, MFSet *S)
{
int i = ;
printf("1)建立一个小顶堆!\n");
//把H->r[1,...,H->length]建成小顶堆
for(i=H->len/; i>=; i--){
HeapAdjust(H, i, H->len);
}
printHeap(H);
printf("2)依次输出堆顶元素并重新调整成小顶堆!\n");
ArcBox tmp;
for(i=H->len; i>; i--){
tmp = H->r[];
H->r[] = H->r[i];
H->r[i] = tmp;
HeapAdjust(H, , i-);
printf("新堆顶的弧信息: (%d,%d) %d", tmp.vex1, tmp.vex2, tmp.weight);
if(mergeMFSet(S, tmp.vex1, tmp.vex2) > -){
printf("\t是最小生成树的弧!\n");
}else{
printf("\n");
}
}
} int main(int argc, char *argv[])
{
int nodes=;
int archs=;
printf("输入顶点数和弧数:");
scanf("%d,%d", &nodes, &archs); printf("\n以MFSet结构存放顶点信息:\n");
MFSet S;
initialMFSet(&S, nodes);
printMFSet(&S); printf("以堆结构存放弧信息:\n");
HeapType H;
initialHeap(&H, &S, archs);
printHeap(&H); printf("对存放了弧信息的堆进行排序,在排序过程中输入最小生成树的边\n");
HeapSort(&H, &S);
return ;
}

最小生成树(克鲁斯卡尔算法)

代码运行

/home/lady/CLionProjects/untitled/cmake-build-debug/untitled
输入顶点数和弧数:6,10 以MFSet结构存放顶点信息:
输入第1个子集:V1
输入第2个子集:V2
输入第3个子集:V3
输入第4个子集:V4
输入第5个子集:V5
输入第6个子集:V6
打印MFSet结构中的数据:
index 1:(data V1, parent -1)
index 2:(data V2, parent -1)
index 3:(data V3, parent -1)
index 4:(data V4, parent -1)
index 5:(data V5, parent -1)
index 6:(data V6, parent -1) 以堆结构存放弧信息:
输入第1条弧信息(顶点1 顶点2 权值):V3,V1,1
输入第2条弧信息(顶点1 顶点2 权值):V3,V2,5
输入第3条弧信息(顶点1 顶点2 权值):V3,V5,6
输入第4条弧信息(顶点1 顶点2 权值):V3,V6,4
输入第5条弧信息(顶点1 顶点2 权值):V3,V4,5
输入第6条弧信息(顶点1 顶点2 权值):V1,V2,6
输入第7条弧信息(顶点1 顶点2 权值):V2,V5,3
输入第8条弧信息(顶点1 顶点2 权值):V5,V6,6
输入第9条弧信息(顶点1 顶点2 权值):V6,V4,2
输入第10条弧信息(顶点1 顶点2 权值):V4,V1,5
打印堆结构中的数据:
index 1: arch:(3,1),weight 1)
index 2: arch:(3,2),weight 5)
index 3: arch:(3,5),weight 6)
index 4: arch:(3,6),weight 4)
index 5: arch:(3,4),weight 5)
index 6: arch:(1,2),weight 6)
index 7: arch:(2,5),weight 3)
index 8: arch:(5,6),weight 6)
index 9: arch:(6,4),weight 2)
index 10: arch:(4,1),weight 5)
对存放了弧信息的堆进行排序,在排序过程中输入最小生成树的边
1)建立一个小顶堆!
打印堆结构中的数据:
index 1: arch:(3,1),weight 1)
index 2: arch:(6,4),weight 2)
index 3: arch:(2,5),weight 3)
index 4: arch:(3,6),weight 4)
index 5: arch:(3,4),weight 5)
index 6: arch:(1,2),weight 6)
index 7: arch:(3,5),weight 6)
index 8: arch:(5,6),weight 6)
index 9: arch:(3,2),weight 5)
index 10: arch:(4,1),weight 5)
2)依次输出堆顶元素并重新调整成小顶堆!
新堆顶的弧信息: (3,1) 1 是最小生成树的弧!
新堆顶的弧信息: (6,4) 2 是最小生成树的弧!
新堆顶的弧信息: (2,5) 3 是最小生成树的弧!
新堆顶的弧信息: (3,6) 4 是最小生成树的弧!
新堆顶的弧信息: (4,1) 5
新堆顶的弧信息: (3,4) 5
新堆顶的弧信息: (3,2) 5 是最小生成树的弧!
新堆顶的弧信息: (5,6) 6
新堆顶的弧信息: (3,5) 6 Process finished with exit code 0

图->连通性->最小生成树(克鲁斯卡尔算法)的更多相关文章

  1. 贪心算法(Greedy Algorithm)之最小生成树 克鲁斯卡尔算法(Kruskal&#39;s algorithm)

    克鲁斯卡尔算法(Kruskal's algorithm)是两个经典的最小生成树算法的较为简单理解的一个.这里面充分体现了贪心算法的精髓.大致的流程能够用一个图来表示.这里的图的选择借用了Wikiped ...

  2. 贪心算法(Greedy Algorithm)最小生成树 克鲁斯卡尔算法(Kruskal&#39;s algorithm)

    克鲁斯卡尔算法(Kruskal's algorithm)它既是古典最低的一个简单的了解生成树算法. 这充分反映了这一点贪心算法的精髓.该方法可以通常的图被表示.图选择这里借用Wikipedia在.非常 ...

  3. 最小生成树--克鲁斯卡尔算法(Kruskal)

    按照惯例,接下来是本篇目录: $1 什么是最小生成树? $2 什么是克鲁斯卡尔算法? $3 克鲁斯卡尔算法的例题 摘要:本片讲的是最小生成树中的玄学算法--克鲁斯卡尔算法,然后就没有然后了. $1 什 ...

  4. 最小生成树-克鲁斯卡尔算法(kruskal's algorithm)实现

    算法描述 克鲁斯卡尔算法是一种贪心算法,因为它每一步都挑选当前最轻的边而并不知道全局路径的情况. 算法最关键的一个步骤是要判断要加入mst的顶点是否会形成回路,我们可以利用并查集的技术来做. 并查集的 ...

  5. 最小生成树之Kruskal(克鲁斯卡尔)算法

    学习最小生成树算法之前我们先来了解下下面这些概念: 树(Tree):如果一个无向连通图中不存在回路,则这种图称为树. 生成树 (Spanning Tree):无向连通图G的一个子图如果是一颗包含G的所 ...

  6. prim算法,克鲁斯卡尔算法---最小生成树

    最小生成树的一个作用,就是求最小花费.要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,因此另一个目标是要使铺设光 ...

  7. 图解最小生成树 - 克鲁斯卡尔(Kruskal)算法

    我们在前面讲过的<克里姆算法>是以某个顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树的.同样的思路,我们也可以直接就以边为目标去构建,因为权值为边上,直接找最小权值的边来构建生成树 ...

  8. 最小生成树——Kruscal(克鲁斯卡尔算法)

    一.核心思想 ​ 将输入的数据由小到大进行排序,再使用并查集算法(传送门)将每个点连接起来,同时求和. ​ 个人认为这个算法比较偏向暴力,有些题可能会超时. 二.例题 洛谷-P3366 题目地址:ht ...

  9. 洛谷P3366【模板】最小生成树-克鲁斯卡尔Kruskal算法详解附赠习题

    链接 题目描述 如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz 输入输出格式 输入格式: 第一行包含两个整数N.M,表示该图共有N个结点和M条无向边.(N<=5000,M&l ...

随机推荐

  1. 【发布iCore3&iCore4ADM资料】

    资料包说明: 1.解压资料包,里面有两个文件夹,iCore3和iCore4. iCore3文件夹里包含源代码及AD模块的详细资料. iCore4文件夹里仅有源代码,AD模块的详细资料参考iCore3里 ...

  2. ambari安装 QA

    1.在安装时 出现Public key for ambari-server-2.4.2.0-136.x86_64.rpm is not installed 安装ambari报错 在安装HST服务时也报 ...

  3. <以交易为生>读书笔记

    为了确保操作成功,就要练习进行防御性资金管理. 长期来看,没有哪位大师能让你发财,你必须自己努力 要坚持写操作日记—记下每次买卖的理由,从中总结出那些反复成功和反复失败的操作模式 大多数交易者无法忍受 ...

  4. 海量数据中找top K专题

    1. 10亿个数中找出最大的1000个数 这种题目就是分治+堆排序. 为啥分治?因为数太多了,全部加载进内存不够用,所以分配到多台机器中,或者多个文件中,但具体分成多少份,视情况而定,只要保证满足内存 ...

  5. 从去除毛刺的策略看开运算opening_circle和闭运算closing_circle的异同

    例一:毛刺在往外凸的面上 策略1:分割出黑色部分,然后通过开运算去掉毛刺,再通过原黑色部分区域减去开运算之后的区域,得到毛刺部分的区域. read_image (Tu, 'C:/Users/xiahu ...

  6. qualcomm wifi kernel beacon loss 日志记录

    记录一下高通对于beacon loss的处理的日志.方便后面再来分析. 04:17:13.397139 [04:17:13.383909] [000000A81699E7EC] [VosMC] wla ...

  7. Eclipse使用心得与技巧

    一. 常用快捷键(熟练使用快捷键可以充分提高编程效率,吐血整理...) 1,Alt + ↑上方向键:向上移动选中的代码,你可以把一行或者一段代码直接上移几行 2,Alt + ↓下方向键:向下移动选中的 ...

  8. 小程序返回顶部top滚动

    wxjs const app = getApp(); Page({ data:{ // top标签显示(默认不显示) backTopValue:false }, // 监听滚动条坐标 onPageSc ...

  9. 和我一起学Effective Java之类和接口

    类和接口 使类和成员的可访问性最小 信息隐藏(information hiding)/封装(encapsulation):隐藏模块内部数据和其他实现细节,通过API和其他模块通信,不知道其他模块的内部 ...

  10. 大杂烩 -- 简析TCP的三次握手与四次分手

    基础大杂烩 -- 目录 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - ...