c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树

时间:2022-11-03 08:39:27

c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树

最小生成树(Minimum Cost Spanning Tree)的概念:

假设要在n个城市之间建立公路,则连通n个城市只需要n-1条线路。这时,自然会考虑,如何在最节省经费的前提下建立这个公路网络。

每2个城市之间都可以设置一条公路,相应地都要付出一定的经济代价。n个城市之间,最多可以设置n(n-1)/2条线路,那么,如何在这些可能的线路中选择n-1条,以使总的耗费最少?

克鲁斯卡尔(kruskal)算法的大致思路:

把每条边的权重按照从小到大排序后,连接。连接时,需要查看要连接的两个顶点的父节点是否相同,不同才可以连接,连接后,更新父节点。

图为下图:

c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树

第一步

图1

c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树

第二步

图2

c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树

第三步

图3

c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树

第四步

A->D,C->D,B->C的权重都是5,这时就不知道连哪个了,所以要创建2个辅助函数is_same,mark_same。

is_same用来判断要连接的2个点的父节点是否相同,如果相同就说明了,连接后,图就存在了环,所以不可以连接,放弃这条边,去寻找下一条边。

mark_same用来更新节点的父节点。

当拿到的节点是AD时,发现AD的父节点都是A,所以放弃;

当拿到的节点是CD时,发现AD的父节点都是A,所以放弃;

当拿到的节点是BC时,发现B的父节点是自己,C的父节点是A,父节点不同,所以连接,并更父节点

图4

c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树

找一半的矩阵,把各条边的起点,终点,权重,放到edge数组里

图5

c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树

mixSpanTree.h

#ifndef __mixspantree__
#define __mixspantree__ #include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <memory.h>
#include <stdlib.h>
#include <stdbool.h> #define Default_vertex_size 20 #define T char//dai biao ding dian de lei xing
#define E int
#define MAX_COST 0x7FFFFFFF typedef struct GraphMtx{
int MaxVertices;//zui da ding dian shu liang]
int NumVertices;//shi ji ding dian shu liang
int NumEdges;//bian de shu lian T* VerticesList;//ding dian list
int** Edge;//bian de lian jie xin xi, bu shi 0 jiu shi 1
}GraphMtx; typedef struct Edge{
int begin;//边的起点
int end; //边的终点
E cost; //边的权重
}Edge; //chu shi hua tu
void init_graph(GraphMtx* gm);
//打印二维数组
void show_graph(GraphMtx* gm);
//插入顶点
void insert_vertex(GraphMtx* gm, T v);
//添加顶点间的线
void insert_edge(GraphMtx* gm, T v1, T v2, E cost); //用kruskal算法构造最小生成树
void minSpanTree_kruskal(GraphMtx* gm); #endif

mixSpanTree.c

#include "mixSpanTree.h"

void init_graph(GraphMtx* gm){
gm->MaxVertices = Default_vertex_size;
gm->NumEdges = gm->NumVertices = 0; //kai pi ding dian de nei cun kong jian
gm->VerticesList = (T*)malloc(sizeof(T) * (gm->MaxVertices));
assert(NULL != gm->VerticesList); //创建二维数组
//让一个int的二级指针,指向一个有8个int一级指针的数组
//开辟一个能存放gm->MaxVertices个int一级指针的内存空间
gm->Edge = (int**)malloc(sizeof(int*) * (gm->MaxVertices));
assert(NULL != gm->Edge);
//开辟gm->MaxVertices组,能存放gm->MaxVertices个int的内存空间
for(int i = 0; i < gm->MaxVertices; ++i){
gm->Edge[i] = (int*)malloc(sizeof(int) * gm->MaxVertices);
}
//初始化二维数组
//让每个顶点之间的边的关系都为不相连的
for(int i = 0; i < gm->MaxVertices; ++i){
for(int j = 0; j < gm->MaxVertices; ++j){
if(i == j)
gm->Edge[i][j] = 0;
else
gm->Edge[i][j] = MAX_COST;
}
}
}
//打印二维数组
void show_graph(GraphMtx* gm){
printf(" ");
for(int i = 0; i < gm->NumVertices; ++i){
printf("%c ", gm->VerticesList[i]);
}
printf("\n");
for(int i = 0; i < gm->NumVertices; ++i){
//在行首,打印出顶点的名字
printf("%c:", gm->VerticesList[i]);
for(int j = 0; j < gm->NumVertices; ++j){
if(gm->Edge[i][j] == MAX_COST){
printf("%c ", '*');
}
else{
printf("%d ", gm->Edge[i][j]);
}
}
printf("\n");
}
printf("\n");
}
//插入顶点
void insert_vertex(GraphMtx* gm, T v){
//顶点空间已满,不能再插入顶点了
if(gm->NumVertices >= gm->MaxVertices){
return;
}
gm->VerticesList[gm->NumVertices++] = v;
} int getVertexIndex(GraphMtx* gm, T v){
for(int i = 0; i < gm->NumVertices; ++i){
if(gm->VerticesList[i] == v)return i;
}
return -1;
}
//添加顶点间的线
void insert_edge(GraphMtx* gm, T v1, T v2, E cost){
if(v1 == v2)return; //查找2个顶点的下标
int j = getVertexIndex(gm, v1);
int k = getVertexIndex(gm, v2);
//说明找到顶点了,并且点之间还没有线
if(j != -1 && k != -1 ){
//因为是无方向,所以更新2个值
gm->Edge[j][k] = gm->Edge[k][j] = cost;
//边数加一
gm->NumEdges++;
}
} //比较边的权重,本函数作为快速排序函数的参数来使用。
int cmp(const void* a, const void* b){
return ((*(Edge*)a).cost - (*(Edge*)b).cost);
}
//判断参数的2个顶点的父节点是否相同
bool is_same(int* father, int begin, int end){
while(father[begin] != begin){
begin = father[begin];
}
while(father[end] != end){
end = father[end];
}
return begin == end;
}
//找到end节点的父节点x,再找到begin节点的父节点y,更新x节点的父节点为y
void mark_same(int* father, int begin, int end){
while(father[begin] != begin){
begin = father[begin];
}
while(father[end] != end){
end = father[end];
}
father[end] = begin; }
//用kruskal算法构造最小生成树
void minSpanTree_kruskal(GraphMtx* g){ int n = g->NumVertices;
Edge* edge = (Edge*)malloc(sizeof(Edge) * n*(n-1)/2);
assert(edge != NULL); int k = 0;
//查找一半的矩阵,把各条边的起点,终点,权重,放到edge数组里,参照上面的图5
for(int i = 0; i < n; ++i){
for(int j = i; j < n; j++){
if(g->Edge[i][j] != 0 && g->Edge[i][j] != MAX_COST){
edge[k].begin = i;
edge[k].end = j;
edge[k].cost = g->Edge[i][j];
k++;
}
}
} //按照权重来排序(用系统函数)
//第一个参数:要被排序的数组
//第二个参数:数组中元素的个数
//第三个参数:每个数组元素占用的内存空间
//第四个参数:函数指针,指定排序的规则
qsort(edge, k, sizeof(Edge), cmp); //初始化每个节点的父节点,让每个节点的父节点为自身
int *father = (int*)malloc(sizeof(int) * n);
assert(NULL != father);
for(int i = 0; i < n; ++i){
father[i] = i;
} for(int i = 0; i < n; ++i){
//判断2个节点的父节点是否相同,不相同就连接
if(!is_same(father, edge[i].begin, edge[i].end)){
printf("%c->%c:%d\n",g->VerticesList[edge[i].begin],g->VerticesList[edge[i].end], edge[i].cost);
//连接后,找到节点end的父节点x,再找到节点begin的父节点y,把节点x的父节点更新为y
mark_same(father, edge[i].begin, edge[i].end);
}
}
}

mixSpanTreemain.c

#include "mixSpanTree.h"

int main(){
GraphMtx gm;
//初始化图
init_graph(&gm);
//插入顶点
insert_vertex(&gm, 'A');
insert_vertex(&gm, 'B');
insert_vertex(&gm, 'C');
insert_vertex(&gm, 'D');
insert_vertex(&gm, 'E');
insert_vertex(&gm, 'F'); //添加连线
insert_edge(&gm, 'A', 'B', 6);
insert_edge(&gm, 'A', 'D', 5);
insert_edge(&gm, 'A', 'C', 1);
insert_edge(&gm, 'B', 'E', 3);
insert_edge(&gm, 'B', 'C', 5);
insert_edge(&gm, 'C', 'E', 6);
insert_edge(&gm, 'C', 'D', 5);
insert_edge(&gm, 'C', 'F', 4);
insert_edge(&gm, 'F', 'E', 6);
insert_edge(&gm, 'D', 'F', 2);
//打印图
show_graph(&gm); //kruskal
minSpanTree_kruskal(&gm); }

完整代码

编译方法:gcc -g mixSpanTree.c mixSpanTreemain.c

c/c++ 用克鲁斯卡尔(kruskal)算法构造最小生成树的更多相关文章

  1. 克鲁斯卡尔&lpar;Kruskal&rpar;算法求最小生成树

    /* *Kruskal算法求MST */ #include <iostream> #include <cstdio> #include <cstring> #inc ...

  2. 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

    图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...

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

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

  4. Kruskal算法构造最小生成树

    Kruskal算法来构造最小生成树,我总结了分为以下步骤: (1)建图,构造Kruskal边集,边集元素应该包括该边的起始顶点.终止顶点.权值: (2)将边集按权值从小到大的顺序进行排序: (3)从小 ...

  5. 克鲁斯卡尔&lpar;Kruskal&rpar;算法

    # include <stdio.h> # define MAX_VERTEXES //最大顶点数 # define MAXEDGE //边集数组最大值 # define INFINITY ...

  6. 图解最小生成树 - 克鲁斯卡尔&lpar;Kruskal&rpar;算法

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

  7. 经典问题----最小生成树(kruskal克鲁斯卡尔贪心算法)

    题目简述:假如有一个无向连通图,有n个顶点,有许多(带有权值即长度)边,让你用在其中选n-1条边把这n个顶点连起来,不漏掉任何一个点,然后这n-1条边的权值总和最小,就是最小生成树了,注意,不可绕成圈 ...

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

    [0]README 0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在 理解 Kruskal(克鲁斯卡尔)算法 的idea 并用 源代码加以实现: 0.2)最小生成树的基础知识,参见 ...

  9. c&sol;c&plus;&plus; 用普利姆(prim)算法构造最小生成树

    c/c++ 用普利姆(prim)算法构造最小生成树 最小生成树(Minimum Cost Spanning Tree)的概念: ​ 假设要在n个城市之间建立公路,则连通n个城市只需要n-1条线路.这时 ...

随机推荐

  1. wordpress对显示某分类目录的文章列表页时对单个文章显示字数限制和省略

    wordpress会在对应的此类列表页面模板中,遍历输出每篇文章的信息,缩略图,作者,正文,标题等, 输出具体的正文的部分 使用这样一句 <?php the_content(''); ?> ...

  2. 设置dt height 保证dd在同一行

    <html>   <head>   <meta charset="UTF-8">   <meta name="Author&qu ...

  3. pyzmq missing when running ipython notebook

    Q: I can run iPython, but when I try to initiate a notebook I get the following error: ~ ipython not ...

  4. ecshop去官方化的修改

    1:如何修改网站"欢迎光临本店" 回答:languages\zh_cn\common.php文件中, $_LANG['welcome'] = '欢迎光临本店';将他修改成你需要的字 ...

  5. CSS 中常用的选择器(选择符)

    一.标签选择器:直接将HTML标签(Tag)作为选择器,可以是p.h1.dl.strong等HTML标签.如: p { font:12px;}em { color:blue;}dl { float:l ...

  6. C51与汇编语言混合编程

    函数内部混合编程 若想在C语言函数内部使用汇编语言,应使用以下Cx51编译器控制命令: #pragma asm ; Assembly code #pragma endasm  功能作用:asm和end ...

  7. 认识元数据和IL(上) &lt&semi;第三篇&gt&semi;

    说在,开篇之前 很早就有说说Metadata(元数据)和IL(中间语言)的想法了,一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀,虽然来得没有<第一回:恩怨情仇:is和as>那么 ...

  8. debian安装vld来查看Opcode&comma;PHP调优。

    一: 我的环境: Debian 7 (wheezy)  x64 PHP 5.4.4-14 (apt-get 而来) Apache/2.2.22 (同上,非源码编译) 二 :安装vld. (# 代表是r ...

  9. 使用oracle数据库,多用户同时对一个表进行增加,删除,修改,查看等操作,会不会有影响?

    使用oracle数据库,多用户同时对一个表进行增加,删除,修改,查看等操作,会不会有影响? 1.问题:各操作间或者性能上会不会有影响? 如果有该如何解决? 多用户操作的影响主要是回锁定记录,oracl ...

  10. VisualStudio2012轻松把JSON数据转换到POCO的代码

    原文:VisualStudio2012轻松把JSON数据转换到POCO的代码       在Visual Studio 2012中轻松把JSON数据转换到POCO的代码,首先你需要安装Web Esse ...