算法学习记录-图——最小生成树之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算法---------------最小生成树 ...
随机推荐
- 洛谷 P1463 [HAOI2007]反素数
https://www.luogu.org/problemnew/show/P1463 注意到答案就是要求1-n中约数最多的那个数(约数个数相同的取较小的) 根据约数个数的公式,在约数个数相同的情况下 ...
- dubbo属性配置
一.覆盖策略 JVM启动-D参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口.XML次之,如果在XML中有配置,则dubbo.properties中的相应配置项无效.P ...
- Windows忘记mysql的密码
1.查看mysql的安装路径 show variables like "%char%"; 路径:C:\Program Files\MySQL\MySQL Server 5.7\ 2 ...
- split命令:文件切割
split命令:文件切割 有时候文件过大,导致不能正常使用,可以用split进行切割. 命令参数: split [选项] [要切割的文件] [输出文件名前缀] -a, --suffix-length= ...
- mybatis内置二级缓存。
一.查询缓存的使用,主要是为了提供查询访问速度.将用户对同一数据的重复查询过程简化, 不再每次均从数据库查询获取结果数据,从而提高访问速度. 二.内置二级缓存... 由于MyBatista从缓存中读取 ...
- 洛谷 P1902 刺杀大使
刺杀大使 一道并不难的二分题,竟让我交了上20次,诶,果然还是我太弱了. 看完题目就基本想到要怎么做了: 只需要对最小伤害代价进行二分即可,check()函数里用搜索判断是否可以到达最后一行,这里的c ...
- 模拟一次CSRF(跨站请求伪造)例子,适合新手
GET请求伪造 有一个论坛网站,网站有一个可以关注用户的接口,但是必须登录的用户才可以关注其他用户. 这个网站的网站是www.a.com 有一天有一个程序员想提高自己的知名度,决定利用CSRF让大家关 ...
- 【Web应用-网络连接】关于 Azure Web 应用 4 分钟空闲连接的限制
Azure Web 应用后台在处理耗时较长的请求时,并且在此期间,客户端和 Azure Web 应用没有数据交互,即 TCP 连接一直处于空闲状态,此种情况超过 4 分钟后,Azure Web 应用会 ...
- 洛谷 P2264 情书
题目背景 一封好的情书需要撰写人全身心的投入.lin_toto同学看上了可爱的卡速米想对她表白,但却不知道自己写的情书是否能感动她,现在他带着情书请你来帮助他. 题目描述 为了帮助lin_toto,我 ...
- strongSwan大坑一直重启(ubuntu)
报错 Starting strongSwan 5.3.2 IPsec [starter]... charon (20533) started after 40 ms charon stopped af ...