45. 蛤蟆的数据结构笔记之四十五克鲁斯卡尔算法

本篇名言:“假如生活欺骗了你 ,不要忧郁 ,
也不要愤慨 !不顺心的时候暂且容忍 :
相信吧 , 快乐的日子就会到来。--普希金”

上两篇学习了弗洛伊德和迪杰特斯拉算法。这次来看下克鲁斯卡尔算法。

欢迎转载,转载请标明出处:http://write.blog.csdn.net/postedit/47071539

1.  克鲁斯卡尔算法

克鲁斯卡尔(Kruskal)算法是在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。是实现图的最小生成树最常用的算法。

1.1             生成树定义

对于有 n 个顶点的无向连通图 G, 把遍历过程中顺序访问的两个顶点之间的路径记录下来,这样G中的n个顶点以及由出发点一次访问其余 n-1 个顶点所经过的 n-1 条边就构成了G 的极小连通子图,也就是 G 的一棵生成树,出发顶点是生成树的根。

1.2             最小生成树

给定一个连通网络, 要求构造具有最小代价的生成树时, 也即使生成树的各边的权值总和达到最小。 把生成树各边的权值总和定义为生成树的权, 那么具有最小权值的生成树就构成了连通网络的最小生成树。

构造最小生成树的算法有很多种,其中大多数的算法都利用了最小生成树的一个性质,

简称为 MST 性质:假设 G=(V,E)是一个连通网络,U 是 V 中的一个真子集,若存在顶点u ∈U 和顶点v∈(V ? U ) 的边(u,v)是一条具有最小权值的边,则必存在 G 的一棵最小生成树包括这条边(u,v)。

构造最小生成树的常用算法主要有:Prim(普里姆)算法和 Kruskal(克鲁斯卡尔)算法。

本篇将的是克鲁斯卡尔算法,下篇我们将普里姆算法。

1.3             克鲁斯卡尔算法

设 G=( V,E )是一个有 n 个顶点的连通图,则令最小生成树的初始状态为只有 n 个顶点而无任何边的非连通图 T={V,{?}},图中每个顶点自成一个连通分量。依次选择 E 中的最小代价边,若该边依附于 T 中的两个不同的连通分量,则将此边加入到 T 中;否则,舍去此边而选择下一条代价最小的边。以此类推,直到 T 中所有顶点都在同一连通分量上为止。这时的 T 就是 G 的一棵最小生成树。

1.4             典型案例

最典型的是一个题目如下:

如下图1所示的赋权图表示某七个城市及预算它们之间的一些某些直接通信道路造价(单位:万元),试给出一个设计方案,使得各城市之间既能够通信又使总造价最小并计算其最小值。

图2的邻接矩阵为

赋权图3中 i,j 两个城市之间的造价费用边记为S ij, ,则从小到大排序

l  步骤1

初始化如下图4

l  步骤2

图5中选择造价最小的边,S17,此时 T={{2,3,4,5,6},{ S17 }},造价 Cost=1

l  步骤3

图6接着选择造价第二小的序号2 的边,即S 34 ,加入 T 中,此时 T={{2,5,6},{ S17 , S34 }},造价Cost=1+3=4

l  步骤4

以此类推,直到选择造价第五小的序号为 5 的边,即S 23,由于加入后边S23,S27,S37 将构成回路,因此舍弃该边.图7

l  步骤5

以此类推,直到所有顶点已包含在树中,整棵最小生成树已经构造完成。如图8

2.  代码实现

2.1         结构体定义

typedefstruct

{

intbegin;

intend;

intweight;

}edge;

typedefstruct

{

intadj;

intweight;

}AdjMatrix[MAX][MAX];

typedefstruct

{

AdjMatrixarc;

intvexnum, arcnum;

}MGraph;

定义边,包括开始、结束和权值。

定义一个矩阵,每个矩阵点包含是否邻接数值以及权值。

定义一个图,包括矩阵和顶点数量。

2.2         main

根据输入来创建图,

然后生成最小生成树。

如下图9

2.3         CreatGraph

构造图。

输入城市和边数。

城市数量就是顶点数量。

根据顶点的数量,将每个顶点间的距离初始化为0。

然后输入城市之间的费用。先输入两个城市的坐标然后输入城市的费用(权值)。

如下图10

2.4         sort

对权值进行排序

输入参数为一个边的数组,图指针。

根据图中边的总数进行处理,

从第一个边开始,和其余的边进行比较,如果大于其它边则调用Swapn函数进行互换。

实现所有边权值从小到大的排列。

2.5         Swapn

将两个边的信息进行互换,包括起点、终点和权值。

2.6         MiniSpanTree

该函数用于生成最小生成树。

定义一个临时边的数组和整型数组。

根据顶点数量(城市数量),来处理所有边的起始点和结束点,以及权值,并保持在临时边数组中。

调用sort函数进行排序。

然后初始化临时的整型数组都为0.

最后循环,根据边的总数量,以当前循环边的起点和终点为参数调用Find函数。

最后得到最小费用值。

2.7         Find

判断整型数值是否大于0。

如果不大于0 ,则返回输入参数的数组下标。

如果大于0 ,则以该值为数组下标继续在数组中查找。

int Find(int *parent,int
f)

{

while (parent[f]> 0)

{

f =parent[f];

}

return f;

}

3.  源码

#include<stdio.h>

#include<stdlib.h>

#defineM20

#defineMAX20

//结构体定义

typedefstruct

{

intbegin;

intend;

intweight;

}edge;

typedefstruct

{

intadj;

intweight;

}AdjMatrix[MAX][MAX];

typedefstruct

{

AdjMatrixarc;

intvexnum, arcnum;

}MGraph;

//函数申明

void CreatGraph(MGraph*);

void sort(edge*,MGraph*);

void MiniSpanTree(MGraph*);

int Find(int*,int );

void Swapn(edge*,int,
int);

void CreatGraph(MGraph *G)//构造图

{

inti, j,n, m;

printf("请输入城市数及边数:");

scanf("%d%d",&G->vexnum,&G->arcnum);

for(i = 1; i <= G->vexnum; i++)//初始化图

{

for (j = 1; j <= G->vexnum; j++)

{

G->arc[i][j].adj= G->arc[j][i].adj = 0;

}

}

printf("请输入有道路连通的 2个城市及他们之间的造价费用(城市
1城市2费用):\n");

for (i = 1; i <= G->arcnum; i++)//输入边和费用

{

scanf("%d%d",&n,&m);

G->arc[n][m].adj= G->arc[m][n].adj = 1;

scanf("%d",&G->arc[n][m].weight);

}

printf("邻接矩阵为:\n");

for (i = 1; i <= G->vexnum; i++)

{

for (j = 1; j <= G->vexnum; j++)

{

if(G->arc[i][j].adj==1)

printf("%d",G->arc[i][j].weight);

elseprintf("%d ",G->arc[i][j].adj);

G->arc[j][i].weight=G->arc[i][j].weight;

}

printf("\n");

}

}

void sort(edgeedges[],MGraph
*G)//对权值进行排序

{

inti, j;

for (i = 1; i < G->arcnum; i++)

{

for (j = i + 1; j <= G->arcnum; j++)

{

if (edges[i].weight>
edges[j].weight)

{

Swapn(edges,i, j);

}

}

}

printf("按造价排序之后的边顺序为(序号边费用):\n");

for(i = 1; i <= G->arcnum; i++)

{

printf("%d.< %d, %d > %d\n", i,edges[i].begin,edges[i].end,edges[i].weight);

}

}

void Swapn(edge *edges,inti,
int
j)

{

inttemp;

temp =edges[i].begin;

edges[i].begin=
edges[j].begin;

edges[j].begin= temp;

temp =edges[i].end;

edges[i].end=
edges[j].end;

edges[j].end= temp;

temp =edges[i].weight;

edges[i].weight=
edges[j].weight;

edges[j].weight= temp;

}

void MiniSpanTree(MGraph *G)//生成最小生成树

{

inti, j, n, m,Mincost=0;

int k= 1;

intparent[M];

edgeedges[M];

for (i = 1; i < G->vexnum; i++)

{

for(j = i + 1; j <= G->vexnum; j++)

{

if (G->arc[i][j].adj== 1)

{

edges[k].begin= i;

edges[k].end= j;

edges[k].weight=G->arc[i][j].weight;

k++;

}

}

}

sort(edges,G);

for(i = 1; i <= G->arcnum; i++)

{

parent[i]= 0;

}

printf("最小生成树为:\n");

for(i = 1; i <= G->arcnum; 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);

Mincost+=edges[i].weight;

}

}

printf("使各城市间能够通信的最小费用为:Mincost=%d\n",Mincost);

}

int Find(int *parent,int
f)

{

while (parent[f]> 0)

{

f =parent[f];

}

return f;

}

void main()//主函数

{

MGraph*G;

G = (MGraph*)malloc(sizeof(MGraph));

CreatGraph(G);

MiniSpanTree(G);

}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-26 01:12:44

45. 蛤蟆的数据结构笔记之四十五克鲁斯卡尔算法的相关文章

44. 蛤蟆的数据结构笔记之四十四弗洛伊德Floyd算法

44. 蛤蟆的数据结构笔记之四十四弗洛伊德Floyd算法 本篇名言:"希望是厄运的忠实的姐妹. --普希金" 我们继续来看下数据结构图中的一个算法,这个算法来自图灵奖得主. 1.  Floyd算法介绍 Floyd算法又称为插点法,是一种用于寻找给定的加权图中多源点之间最短路径的算法.该算法名称以创始人之一.1978年图灵奖获得者.斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名.注意这个可不是心理学的那个弗洛伊德. 是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先 本篇名言:"生活真象这杯浓酒 ,不经三番五次的提炼呵 , 就不会这样一来可口 ! -- 郭小川" 继续看下广度优先的遍历,上篇我们看了深度遍历是每次一个节点的链表是走到底的. 欢迎转载,转载请标明出处:http://write.blog.csdn.net/postedit/47029275 1.  原理 首先,从图的某个顶点v0出发,访问了v0之后,依次访问与v0相邻的未被访问的顶点,然后分别从这些顶点出发,广度优先遍历,直至所有的

48. 蛤蟆的数据结构笔记之四十八的有向无环图的应用关键路径

48. 蛤蟆的数据结构笔记之四十八的有向无环图的应用关键路径 本篇名言:"富贵不淫贫贱乐 ,男儿到此是豪雄.-- 程颢" 这次来看下有向无环图的另一个应用关键路径. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47135061 1.  关键路径 与AOV-网相对应的是AOE-网(Activity On Edge)即边表示活动的网.AOE-网是一个带权的有向无环图,其中,顶点表示事件(Event),弧表示活动,权表

49. 蛤蟆的数据结构笔记之四十九图的连通性问题

49. 蛤蟆的数据结构笔记之四十九图的连通性问题 本篇名言:"我们不得不饮食.睡眠.游惰.恋爱,也就是说,我们不得不接触生活中最甜蜜的事情:不过我们必须不屈服于这些事物 .....--约里奥?居里"     此篇就作为数据结构入门笔记的最后一篇吧. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47135259 设图G=(V,E)是一个无向图,G的一个连通分支是一个最大的连通子图,即一个连通分支是不包含在任何更大的

46. 蛤蟆的数据结构笔记之四十六普里姆算法

46. 蛤蟆的数据结构笔记之四十六普里姆算法 本篇名言:"手莫伸 ,伸手必被捉.党与人民在监督 ,万目睽睽难逃脱.汝言惧捉手不伸 ,他道不伸能自觉 , 其实想伸不敢伸 ,人民咫尺手自缩.-- 陈毅" 连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边.所谓的最小成本,就是n个顶点,用n-1条边把一个连通图连接起来,并且使得权值的和最小.构造连通网的最小代价生成树,即最小生成树(Minimum Cost Spanning Tree). 找连通图的最

15、蛤蟆的数据结构笔记之十五栈的应用之栈与递归之八皇后问题

15.蛤蟆的数据结构笔记之十五栈的应用之栈与递归之八皇后问题 本篇名言:"人的一生应当这样度过:当回忆往事的时候,他不致于因为虚度年华而痛悔,也不致于因为过去的碌碌无为而羞愧:在临死的时候,他能够说:"我的整个生命和全部精力,都已经献给世界上最壮丽的事业--为人类的解放而斗争." 继续递归问题,本次是经典的八皇后问题: 欢迎转载,转载请标明出处: 1.  八皇后问题 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出

35. 蛤蟆的数据结构笔记之三十五遍历二叉树

35. 蛤蟆的数据结构笔记之三十五遍历二叉树 本篇名言:"冬天已经到来,春天还会远吗? --雪莱" 我们来看徐璈如何遍历二叉树. 欢迎转载,转载请标明出处: 1.  二叉树遍历 二叉树的遍历有三种方式,如下: (1)前序遍历(DLR),首先访问根结点,然后遍历左子树,最后遍历右子树.简记根-左-右. (2)中序遍历(LDR),首先遍历左子树,然后访问根结点,最后遍历右子树.简记左-根-右. (3)后序遍历(LRD),首先遍历左子树,然后遍历右子树,最后访问根结点.简记左-右-根. 2.

16、蛤蟆的数据结构笔记之十六栈的应用之栈与递归之汉诺塔问题

16.蛤蟆的数据结构笔记之十六栈的应用之栈与递归之汉诺塔问题 本篇名言:"人生的价值,并不是用时间,而是用深度去衡量的." 继续栈与递归应用,汉诺塔问题. 欢迎转载,转载请标明出处: 1.  汉诺塔问题 汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上.并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一

41 蛤蟆的数据结构笔记之四十一图的遍历之深度优先

41  蛤蟆的数据结构笔记之四十一图的遍历之深度优先 本篇名言:"对于我来说 , 生命的意义在于设身处地替人着想 , 忧他人之忧 , 乐他人之乐. -- 爱因斯坦" 上篇我们实现了图的邻接多重表表示图,以及深度遍历和广度遍历的代码,这次我们先来看下图的深度遍历. 欢迎转载,转载请标明出处: 1.  原理 图遍历又称图的遍历,属于数据结构中的内容.指的是从图中的任一顶点出发,对图中的所有顶点访问一次且只访问一次.图的遍历操作和树的遍历操作功能相似.图的遍历是图的一种基本操作,图的许多其它