算法学习记录-图——最小生成树之Kruskal算法
之前的Prim算法是基于顶点查找的算法,而Kruskal则是从边入手。
通俗的讲:就是希望通过 边的权值大小 来寻找最小生成树。(所有的边称为边集合,最小生成树形成的过程中的顶点集合称为W)
选取边集合中权值最小的边,查看边的两个顶点是否能和集合W构成环路,若能构成环路,则舍去;否则选取下一条最小权值边重复上一步。
这里需要注意一个问题,我们从最小权值的边开始寻找最小生成树,
判断当即将选入的边的两个顶点是否会和已经在集合中的顶点构成环路,这个是我们需要解决的问题。
先说下Kruskal算法的数学语言:
假设连通网N={V,{E}},则令最小生成树的初始状态只有n个顶点而无边的非连通图(T=(V,{})),图中每个顶点自成一个连通分量。
在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。
依次类推,直到T中所有顶点都在同一连通分量上为止。
过程:












问题:
1.边的存储和排序。
2.判断新边是否会形成环路。
问题1比较好解决。可以用一个结构来存放,然后从小到大排序。
问题2比较麻烦:
怎么通过一个方法来判断新加入的边是否对已经归纳好的边形成环路。总是有牛人想出一些非常巧妙的方法,向他们致敬。
以我的图为例,我第一次出现环路的边是在第五幅图:

如果我们建立边的结构时候,将下标数字小的作为起点,数字大的作为终点。比如边(v5,v6)存放格式为
start:5,end:6,weight:3。
同理边(v4,v6)
start:4,end:6,weight:5。
当我们找到边(v4,v5)的时候,怎么可以让这条线被舍弃。刚才我们设置的路线上看,v4——>v6,v5——>v6是否可以通过这样的指向方式来确定两个点是否连通?
这样,如果v4和v5的边被选中,则会出现 v4,v5,v6三个点构成环路。这样我们可以用一个数组来指示Vx点所指示下一个点的下标,递归的遍历该下标,直到找到连接该点的终点。
所以,当一个边是否被选入,此时递归寻找他的两个顶点最终的指向,如果不相等则表示不会构成回路;若相等,则表示构成了回路,需要舍弃。

上图中的箭头线是表示该顶点记录指向的顶点。旁边说明: 比如 e在图中是由(v6,v7),而实际上是边(v4,v7)。
比如边(v2,v3),v3->v7->v2 与 v2 相等了,所以要舍弃这条边。
代码:
int findsrc(int i)
{ while (g_vexto[i])
{
i = g_vexto[i];
}
return i;
} void kruskal(MGraph g,Edges *p)
{
int i,j;
int length=;
int m,n;
for (i=;i<g.numEdges;i++)
{
m = p[i].start;
n = p[i].end;
printf("(%d,%d)",m,n); m=findsrc(m);
n=findsrc(n);
printf("find:(%d,%d)",m,n);
if (m != n)
{
g_vexto[m]=n;
length += p[i].weight;
printf("--->(%d,%d)--->\n",p[i].start,p[i].end);
}
else
{
printf("\n");
}
}
printf("length:%d\n",length);
}
这里调用的前提是 Edges *p 是按照权值的递增排列的。
按照之前讲解的过程,编写不难。
完整代码:
// grp-kruskal.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <stdlib.h> #define MAXVEX 100
#define IFY 65535 typedef char VertexType;
typedef int EdgeType; bool g_visited[MAXVEX];
int g_vexto[MAXVEX]; VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I'}; EdgeType g_init_edges[MAXVEX][MAXVEX] = {
{,,IFY,IFY,IFY,,IFY,IFY,}, //'A'
{,,,IFY,IFY,IFY,IFY,IFY,}, //'B'
{IFY,,,,IFY,IFY,IFY,IFY,IFY},//'C'
{IFY,IFY,,,,IFY,IFY,IFY,IFY},//'D'
{IFY,IFY,IFY,,,,,,IFY}, //'E'
{,IFY,IFY,IFY,,,,IFY,IFY}, //'F'
{IFY,IFY,IFY,IFY,,,,IFY,IFY}, //'G'
{IFY,IFY,IFY,IFY,,IFY,IFY,,IFY}, //'H'
{,,IFY,IFY,IFY,IFY,IFY,IFY,}, //'I'
}; //==========================================================================
//静态图-邻接矩阵
typedef struct {
VertexType vexs[MAXVEX];
EdgeType Mat[MAXVEX][MAXVEX];
int numVexs,numEdges;
}MGraph; typedef struct {
EdgeType weight;
int start;
int end;
}Edges;
//====================================================================
//打印矩阵
void prt_maxtix(EdgeType *p,int vexs)
{
int i,j;
for (i=;i<vexs;i++)
{
printf("\t");
for (j=;j<vexs;j++)
{
if( (*(p + MAXVEX*i + j)) == IFY)
{
printf(" $ ");
}
else
{
printf(" %2d ", *(p + MAXVEX*i + j));
}
}
printf("\n");
}
} //check the number of vextex
int getVexNum(VertexType *vexs)
{
VertexType *pos = vexs;
int cnt=;
while(*pos <= 'Z' && *pos >= 'A')
{
cnt++;
pos++;
}
return cnt;
} bool checkMat(EdgeType *p,VertexType numvex)
{
int i,j;
for (i=;i<numvex;i++)
{
for(j=i+;j<numvex;j++)
{
//printf("[%d][%d] = %d\t",i,j,*(p + MAXVEX*i + j));
//printf("[%d][%d] = %d\n",j,i,*(p + MAXVEX*j + i));
if (*(p + MAXVEX*i + j) != *(p + MAXVEX*j +i) )
{
printf("ERROR:Mat[%d][%d] or Mat[%d][%d] not equal!\n",i,j,j,i);
return false;
}
}
}
return true;
} void init_Grp(MGraph *g,VertexType *v,EdgeType *p)
{
int i,j;
// init vex num
(*g).numVexs = getVexNum(v); //init vexter
for (i=;i<(*g).numVexs;i++)
{
(*g).vexs[i]=*v;
v++;
} //init Mat
for (i=;i<(*g).numVexs;i++)
{
for (j=;j<(*g).numVexs;j++)
{
(*g).Mat[i][j] = *(p + MAXVEX*i + j);
}
}
if(checkMat(&((*g).Mat[][]),(*g).numVexs) == false)
{
printf("init error!\n");
exit();
}
} void CountEdge(MGraph *g)
{
int i,j;
int cnt=;
for (i=;i<(*g).numVexs;i++)
{
for (j=;j<(*g).numVexs;j++)
{ if ((*g).Mat[i][j] == )
{
break;
}
else
{
if ((*g).Mat[i][j] != IFY)
{
//printf("[%d][%d]=%d\n",i,j,(*g).Mat[i][j]);
cnt++;
}
}
}
}
(*g).numEdges=cnt;
printf("num edges is %d\n",cnt);
} void TranMatToEdges(MGraph g,Edges **e)
{
int i,j;
int cnt=;
*e = (Edges*)malloc(g.numEdges*sizeof(Edges)); for (i=;i<g.numVexs;i++)
{
for (j=;j<g.numVexs;j++)
{
if (g.Mat[i][j] == )
{
break;
}
else
{
if (g.Mat[i][j] != IFY)
{
(*e)[cnt].start=j;
(*e)[cnt].end = i;
(*e)[cnt].weight = g.Mat[i][j];
cnt++;
}
}
}
}
}
void sortByWeight(Edges **edge,int len)
{
int i,j;
Edges temp;
for (i=;i<len;i++)
{
for (j=i+;j<len;j++)
{
//printf("cmp:%d,%d\n",(*edge)[i].weight,(*edge)[j].weight);
if ((*edge)[i].weight > (*edge)[j].weight)
{ temp = (*edge)[j]; (*edge)[j]=(*edge)[i]; (*edge)[i]=temp; }
}
}
} int findsrc(int i)
{ while (g_vexto[i])
{
i = g_vexto[i];
}
return i;
} void kruskal(MGraph g,Edges *p)
{
int i,j;
int length=;
int m,n;
for (i=;i<g.numEdges;i++)
{
m = p[i].start;
n = p[i].end;
printf("(%d,%d)",m,n); m=findsrc(m);
n=findsrc(n);
printf("find:(%d,%d)",m,n);
if (m != n)
{
g_vexto[m]=n;
length += p[i].weight;
printf("--->(%d,%d)--->\n",p[i].start,p[i].end);
}
else
{
printf("\n");
}
}
printf("length:%d\n",length);
}
int _tmain(int argc, _TCHAR* argv[])
{
MGraph grp;
//init
init_Grp(&grp,g_init_vexs,&g_init_edges[][]); Edges *pedges=NULL;
//print Matix
prt_maxtix(&grp.Mat[][],grp.numVexs); CountEdge(&grp);
TranMatToEdges(grp,&pedges);
int i; sortByWeight(&pedges,grp.numEdges);
for (i=;i<grp.numEdges;i++)
{
printf("(%d,%d),%d\n",pedges[i].start,pedges[i].end,pedges[i].weight);
}
printf("kruskal\n");
kruskal(grp,pedges); getchar();
return ;
}
结果:

算法学习记录-图——最小生成树之Kruskal算法的更多相关文章
- 算法学习记录-图——最小生成树之prim算法
一个连通图的生成树是一个极小的连通子图,它包含图中全部的顶点(n个顶点),但只有n-1条边. 最小生成树:构造连通网的最小代价(最小权值)生成树. prim算法在严蔚敏树上有解释,但是都是数学语言,很 ...
- 算法学习记录-图——最短路径之Dijkstra算法
在网图中,最短路径的概论: 两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点. 维基百科上面的解释: 这个算法是通过为每个顶点 v 保留目前为止所找到的从 ...
- 算法学习记录-图(DFS BFS)
图: 目录: 1.概念 2.邻接矩阵(结构,深度/广度优先遍历) 3.邻接表(结构,深度/广度优先遍历) 图的基本概念: 数据元素:顶点 1.有穷非空(必须有顶点) 2.顶点之间为边(可空) 无向图: ...
- 算法学习记录-图——应用之关键路径(Critical Path)
之前我们介绍过,在一个工程中我们关心两个问题: (1)工程是否顺利进行 (2)整个工程最短时间. 之前我们优先关心的是顶点(AOV),同样我们也可以优先关心边(同理有AOE).(Activity On ...
- 算法学习记录-图——最小路径之Floyd算法
floyd算法: 解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包. 设为从到的只以集合中的节点为中间节点的最短路径的长度. 若最短路径经过 ...
- 算法学习记录-图——应用之拓扑排序(Topological Sort)
这一篇写有向无环图及其它的应用: 清楚概念: 有向无环图(DAG):一个无环的有向图.通俗的讲就是从一个点沿着有向边出发,无论怎么遍历都不会回到出发点上. 有向无环图是描述一项工程或者系统的进行过程的 ...
- 最小生成树的Kruskal算法实现
最近在复习数据结构,所以想起了之前做的一个最小生成树算法.用Kruskal算法实现的,结合堆排序可以复习回顾数据结构.现在写出来与大家分享. 最小生成树算法思想:书上说的是在一给定的无向图G = (V ...
- 数据结构与算法--最小生成树之Kruskal算法
数据结构与算法--最小生成树之Kruskal算法 上一节介绍了Prim算法,接着来看Kruskal算法. 我们知道Prim算法是从某个顶点开始,从现有树周围的所有邻边中选出权值最小的那条加入到MST中 ...
- 23最小生成树之Kruskal算法
图的最优化问题:最小生成树.最短路径 典型的图应用问题 无向连通加权图的最小生成树 有向/无向加权图的最短路径 四个经典算法 Kruskal算法.Prim算法---------------最小生成树 ...
随机推荐
- centos7安装iptables
使用CentOS 7时发现使用iptables防火墙时提示错误Unit iptables.service failed to load,意思是防火墙运行启动失败了,那么要如何处理呢. 一直用Cen ...
- PHP使用Socket发送字节流
例如,需要发送以下数据 struct header { int type; // 消息类型 int length; // 消息长度 } struct MSG_Q2R2DB_PAYRESULT { ...
- 一个简单的注册页面,基于JS
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- JSP界面设置提示浮动框
1.公共js <script type="text/javascript"> var tip={ $:function(ele){ if(typeof(ele)==&q ...
- Android镜像文件ramdisk.img,system.img,userdata.img介绍
Android 源码编译后,在out目录下生成的三个镜像文件:ramdisk.img,system.img,userdata.img以及它们对应的目录树root,system,data. ramdis ...
- log4sql介绍
log4sql介绍log4j环境中简单配置的情况下可收集执行的SQL语句和JDBC执行情况,如预编译的”?“显示成参数的实际值 下载log4sql.jar第一步:http://log4sql.sour ...
- 【Web应用】JAVA网络上传大文件报500错误
问题描述 当通过 JAVA 网站上传大文件,会报 500 错误. 问题分析 因为 Azure 的 Java 网站都是基于 IIS 转发的,所以我们需要关注 IIS 的文件上传限制以及 requestT ...
- UVA 10572 Black & White (状压DP)
题意:有一个n*m的矩阵,其中部分格子已经涂黑,部分涂白,要求为其他格子也上黑/白色,问有多少种涂法可以满足一下要求: (1)任意2*2的子矩阵不可以同色. (2)所有格子必须上色. (3)只能有两个 ...
- Mybatis-Generator逆向生成Po,Mapper,XMLMAPPER(idea)
前文有一篇手工生成的说明,地址: http://www.cnblogs.com/xiaolive/p/4874605.html, 现在这个补充一下在idea里面的自动版本的数据库逆向生成工具: 一.g ...
- 操作系统项目:向Linux内核添加一个系统调用
内容: 向Linux增加一个系统调用 撰写一个应用测试程序调用该系统调用 使用ptrace或类似的工具对该测试程序进行跟踪调 环境: 1.vmware workstation 15.0.0 2.ubu ...