最近在复习数据结构,所以想起了之前做的一个最小生成树算法。用Kruskal算法实现的,结合堆排序可以复习回顾数据结构。现在写出来与大家分享。

  最小生成树算法思想:书上说的是在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T 为 G 的最小生成树。说白了其实就是在含有 n 个顶点的连通网中选择 n-1 条边,构成一棵极小连通子图,并使该连通子图中 n-1 条边上权值之和达到最小,则称最小生成树

  本程序用的是克鲁斯卡尔算法(Kruskal),也可以使用prim算法实现。Kruskal思想是在带权连通图中,不断地在排列好的边集合中找到最小的边,如果该边满足得到最小生成树的条件,就将其构造,直到最后得到一颗最小生成树。

图的顶点存储结构体:

 //结构体定义,储存图的顶点
typedef struct {
int from; //边的起始顶点
int to; //边的终止顶点
int cost; //边的权值
}Edge;

问题:顶点编号的类型。

  好的程序应该可以扩展,不论顶点用0,1,2...  顺序编号还是用5,2,1,7... 乱序编号还是用a,b,c...  英文编号都应该可以做到兼容通过,所以在存储图的节点的时候我做了一个映射,就是不论输入的什么编号一律转换成顺序编号0,1,2...,在最后输出的时候再把编号映射回原来的编号,这样就可以应对不同而顶点编号。如下面程序:

 for(i = ;i < edgeNum; i++){
printf("请输入第 %d 条边!\n",i+);
scanf(" %c %c %d",&from,&to,&cost);
edge_temp[i][] = judge_num(from);
edge_temp[i][] = judge_num(to);
edge_temp[i][] = cost;
}
//对输入的边和点信息进行堆排序
HeapSort(edge_temp,edgeNum);
int j;
for(j = ;j < edgeNum; j++){
edge[j].from = edge_temp[j][];
edge[j].to = edge_temp[j][];
edge[j].cost = edge_temp[j][];
}

每次输入顶点后都会先保存到临时存储数组edge_temp中,进行堆排序后再把数据白存在真正的数据中。其中判断是否形成回路借助了一个递归方法:

 //用于判断是否形成回路
int judge(int num){
if(num == judge_temp[num]){
return judge_temp[num];
}
return judge_temp[num] = judge(judge_temp[num]);
}

执行步骤:

1:在带权连通图中,将边的权值排序(本程序用的是堆排序);

2:判断是否需要选择这条边(此时的边已按权值从小到大排好序)。判断的依据是边的两个顶点是否已连通,如果连通则继续下一条;如果不连通,那么就选择使其连通。

3:循环第二步,直到图中所有的顶点都在同一个连通分量中,即得到最小生成树。

判断法则:(当将边加入到已找到边的集合中时,是否会形成回路?)

1:如果没有形成回路,那么直接将其连通。此时,对于边的集合又要做一次判断:这两个点是否在已找到点的集合中出现过?如果两个点都没有出现过,那么将这 两个点都加入已找到点的集合中;如果其中一个点在集合中出现过,那么将另一个没有出现过的点加入到集合中;如果这两个点都出现过,则不用加入到集合中。

2:如果形成回路,不符合要求,直接进行下一次操作。

重点类容就这么多,下面给出源程序,程序直接复制后可以运行,有兴趣的朋友也可以用prim算法实现。

 #include <stdio.h>
#include <string.h>
//常量定义,边点最大数量限制50;
#define MAXE 52 /*
* Info:最小生成树算法源码(C语言版)
* @author: zhaoyafei
* time: 2015
*/ //结构体定义,储存图的顶点
typedef struct {
int from; //边的起始顶点
int to; //边的终止顶点
int cost; //边的权值
}Edge; int nodeNum; //顶点数;
int edgeNum; //边数;
int min_cost; //记录最小生成树(权值)
int judge_temp[MAXE]; //记录判断是否成环
int sort[MAXE][MAXE]; //用来做排序
int edge_temp[MAXE][]; //用于存储堆排序边点信息 Edge edge[MAXE]; //用于存储边点信息
Edge min_edge[MAXE]; //用于存储最小生成树边点信息 char judge_num_int[MAXE];
int inputs = ;
void HeapSort(int array[MAXE][],int length);
int judge_num(char from); //save_point()函数,存储图的点边信息;
void save_point(){
char from;
char to;
int cost = ;
int i;
for(i = ;i < edgeNum; i++){
printf("请输入第 %d 条边!\n",i+);
scanf(" %c %c %d",&from,&to,&cost); edge_temp[i][] = judge_num(from);
edge_temp[i][] = judge_num(to);
edge_temp[i][] = cost;
}
//对输入的边和点信息进行堆排序
HeapSort(edge_temp,edgeNum);
int j;
for(j = ;j < edgeNum; j++){
edge[j].from = edge_temp[j][];
edge[j].to = edge_temp[j][];
edge[j].cost = edge_temp[j][];
}
} int judge_num(char str){
int n1 = ;
for(int j1 = ;j1 < edgeNum * ; j1++){
if(str == judge_num_int[j1]){
n1++;
}
}
if(n1 == ){
judge_num_int[inputs] = str;
inputs++;
}
int return_num = ;
for(int j2 = ;j2 < edgeNum * ; j2++){
if(str == judge_num_int[j2]){
return_num = j2;
}
}
return return_num;
} //用于判断是否形成回路
int judge(int num){
if(num == judge_temp[num]){
return judge_temp[num];
}
return judge_temp[num] = judge(judge_temp[num]);
} //判断是否是一棵最小生成树
bool is_judge(){
int oneedge = judge();
int i;
for(i = ;i <= nodeNum; i++) {
if(oneedge != judge(i)){
return false;
}
}
return true;
} //kruskal算法
void kruskal(){
min_cost = ;//最小生成树
//初始化辅助回路判断数组
int m;
for(m = ;m < MAXE;m++) {
judge_temp[m] = m;
} int edge_num = ;//记录最小生成树的边数
int i;
for(i = ;i < edgeNum; i++){
//小于总节点数
if(edge_num != nodeNum - ){
int edge_from = judge(edge[i].from);
int edge_to = judge(edge[i].to);
//如果形成回路则edge_from == edge_to;
if(edge_from != edge_to){
//如果没有形成回路,则改变原临时数组中的值
judge_temp[edge_from] = edge_to;
min_cost += edge[i].cost; //将符合的边加入到存储数组中
min_edge[edge_num].from = edge[i].from;
min_edge[edge_num].to = edge[i].to;
min_edge[edge_num].cost = edge[i].cost; edge_num++;
}
}
}
} //array是待调整的堆数组,i是待调整的数组元素的位置,nlength是数组的长度
//根据数组array构建大顶堆
void HeapAdjust(int array[MAXE][],int i,int nLength){
int nChild;
for(; *i + < nLength; i = nChild){ //子结点的位置=2*(父结点位置)+1
nChild = *i + ;
//得到子结点中较大的结点
if(nChild < nLength- && array[nChild+][] > array[nChild][]){
++nChild;
}
//如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
if(array[i][] < array[nChild][]){
int temp_arr2[];
temp_arr2[] = array[i][];
temp_arr2[] = array[i][];
temp_arr2[] = array[i][]; array[i][] = array[nChild][];
array[i][] = array[nChild][];
array[i][] = array[nChild][]; array[nChild][] = temp_arr2[];
array[nChild][] = temp_arr2[];
array[nChild][] = temp_arr2[];
}else{
break;//否则退出循环
}
}
} //堆排序算法
void HeapSort(int array[MAXE][],int length){
//调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
//length/2-1是最后一个非叶节点,此处"/"为整除
int j;
for( j= length/ - ; j >= ; --j){
HeapAdjust(array,j,length);
}
//从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
int i;
for(i = length - ; i > ; --i){
//把第一个元素和当前的最后一个元素交换,
//保证当前的最后一个位置的元素都是在现在的这个序列之中最大的
int temp_arr1[]; //构建二维数组的原因:交换后保证数组中其他属性值同时交换
temp_arr1[] = array[i][];
temp_arr1[] = array[i][];
temp_arr1[] = array[i][]; array[i][] = array[][];
array[i][] = array[][];
array[i][] = array[][]; array[][] = temp_arr1[];
array[][] = temp_arr1[];
array[][] = temp_arr1[]; //不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
HeapAdjust(array,,i);
}
} //输出最小生成树的信息(包括边点和权值)
void output(){
if(is_judge()){
printf("最小生成树:\n");
printf("起点 -> 终点 路径长:\n");
for(int i = ;i < nodeNum-; i++){
printf(" %c -> %c %d\n",judge_num_int[min_edge[i].from],judge_num_int[min_edge[i].to],min_edge[i].cost);
}
printf("min cost is : %d\n",min_cost);
printf("*******************************************************************************\n");
printf("请输入 节点数 边数(中间需用空格隔开):\n");
}else{
printf("最小生成树不存在!\n");
printf("*******************************************************************************\n");
printf("请输入 节点数 边数(中间需用空格隔开):\n");
}
} /*
* 程序主方法;
* 用于开始程序,介绍程序操作须知;
*/
int main(){
printf("*******************************************************************************\n");
printf("** 最小生成树(kruskal算法) ***\n");
printf("** 说明:开始程序输入图的总点数和总边数,测试程序目前边点限制最多输入50个 ***\n");
printf("** 中间用空格隔开。输入边和点信息,需要按格式:开始边 终止边 权值 ***\n");
printf("** 本次计算结束可以按要求开始下次计算。 ***\n");
printf("*******************************************************************************\n");
printf("请输入 节点数 边数(中间需用空格隔开):\n");
while(scanf("%d%d",&nodeNum,&edgeNum) != EOF){
//判断输入的边和点的合法性
if(nodeNum < || edgeNum < ){
printf("输入的数据不合法\n");
printf("请输入 节点数 边数(中间需用空格隔开):\n");
return ;
}else if(nodeNum > || edgeNum > ){
printf("输入的边或者点不能大于50\n");
printf("请输入 节点数 边数(中间需用空格隔开):\n");
return ;
}else{
printf("请输入 开始节点 终止节点 该边的权值(中间需用空格隔开,回车换行):\n");
printf("共 %d 条边\n",edgeNum);
for(int m = ;m < MAXE; m++) {
judge_num_int[m] = '-';
}
inputs = ;
save_point(); //存储边点信息
kruskal(); //算法执行
output(); //输出执行结果
}
}
return ;
}

运行结果如下:

最小生成树的Kruskal算法实现的更多相关文章

  1. 数据结构与算法--最小生成树之Kruskal算法

    数据结构与算法--最小生成树之Kruskal算法 上一节介绍了Prim算法,接着来看Kruskal算法. 我们知道Prim算法是从某个顶点开始,从现有树周围的所有邻边中选出权值最小的那条加入到MST中 ...

  2. 邻接矩阵c源码(构造邻接矩阵,深度优先遍历,广度优先遍历,最小生成树prim,kruskal算法)

    matrix.c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include < ...

  3. HDU1875——畅通工程再续(最小生成树:Kruskal算法)

    畅通工程再续 Description相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现.现在政府决定大力发展百岛湖,发展首先要解决的问题当 ...

  4. 23最小生成树之Kruskal算法

    图的最优化问题:最小生成树.最短路径 典型的图应用问题 无向连通加权图的最小生成树 有向/无向加权图的最短路径 四个经典算法 Kruskal算法.Prim算法---------------最小生成树 ...

  5. 最小生成树的Kruskal算法

        库鲁斯卡尔(Kruskal)算法是一种按照连通网中边的权值递增的顺序构造最小生成树的方法.Kruskal算法的基本思想是:假设连通网G=(V,E),令最小生成树的初始状态为只有n个顶点而无边的 ...

  6. 算法学习记录-图——最小生成树之Kruskal算法

    之前的Prim算法是基于顶点查找的算法,而Kruskal则是从边入手. 通俗的讲:就是希望通过 边的权值大小 来寻找最小生成树.(所有的边称为边集合,最小生成树形成的过程中的顶点集合称为W) 选取边集 ...

  7. 图论之最小生成树之Kruskal算法

    Kruskal算法,又称作为加边法,是配合并查集实现的. 图示: 如图,这是一个带权值无向图我们要求它的最小生成树. 首先,我们发现在1的所有边上,连到3的边的边权值最小,所以加上这条边. 然后在3上 ...

  8. 【最小生成树之Kruskal算法】

    看完之后推荐再看一看[最小生成树之Prim算法]-C++ 定义:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边.最小生成树可以用kr ...

  9. 【转载】最小生成树之Kruskal算法

    给定一个无向图,如果它任意两个顶点都联通并且是一棵树,那么我们就称之为生成树(Spanning Tree).如果是带权值的无向图,那么权值之和最小的生成树,我们就称之为最小生成树(MST, Minim ...

随机推荐

  1. python re 模块和基础正则表达式

    1.迭代器:对象在其内部实现了iter(),__iter__()方法,可以用next方法实现自我遍历. 二.python正则表达式 1.python通过re模块支持正则表达式 2.查看当前系统有哪些p ...

  2. bzoj 2527: [Poi2011]Meteors 整体二分

    给每个国家建一个链表,这样分治过程中的复杂度就和序列长度线形相关了,无脑套整体二分就可以. (最坑的地方是如果所有位置都是一个国家,那么它的样本个数会爆longlong!!被这个坑了一次,大于p[i] ...

  3. 了解了下 Google 最新的 Fuchsia OS

    就是看到篇报道,有点好奇,就去FQ挖了点东西回来. 我似乎已开始就抓到了重点,没错,就是 LK . LK 是 Travis Geiselbrecht 写的一个针对 ARM 的嵌入式操作系统,开源的.点 ...

  4. linux之svn

    sudo apt-get install subversion 实践出真理

  5. <<< sqlserver评估过期解决

    点击开始-所有程序-Microsoft SQL Server 2008-配置工具-SQL Server 安装中心然后点击左侧的维护,在点击右侧的版本升级,接着按照提示一直点下一步,到产品密钥的时候输入 ...

  6. DOM0级事件处理和DOM2级事件处理

    转自:http://www.cnblogs.com/holyson/p/3914406.html 0级DOM 分为2个:一是在标签内写onclick事件  二是在JS写onlicke=function ...

  7. win7电脑怎么修改计算机用户名Administrator

    ----------------------------------- 首先,在开始中打开我的控制面板.----->>打开用户账户和家庭安全选项.----->>,继续点击用户账 ...

  8. php操作mongodb

    <?php set_time_limit(0); $mongo = new Mongo('192.168.33.50:27017'); //连接远程主机22011端口 $db = $mongo- ...

  9. C# 的tcp Socket设置自定义超时时间

    简单的c# TCP通讯(TcpListener) C# 的TCP Socket (同步方式) C# 的TCP Socket (异步方式) C# 的tcp Socket设置自定义超时时间 C# TCP ...

  10. 大熊君JavaScript插件化开发------(实战篇之DXJ UI ------ ItemSelector)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,还记得前两篇文章吗.主要讲述了以“jQuery的方式如何开发插件”,以及过程化设计与面向对象思想设计相结合的方式是 如何设计一个插件的,两种方式各有利 ...