算法学习记录-图——最小生成树之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算法---------------最小生成树 ...
随机推荐
- ubuntu dpkg命令总结
dpkg是Debian系统的后台包管理器,类似RPM.也是Debian包管理系统的中流砥柱,负责安全卸载软件包,配置,以及维护已安装的软件包.由于ubuntu和Debian乃一脉相承,所以很多命令是不 ...
- Spark Mllib里如何将trainDara训练数据文件里第一行是字段名不是数据给删除掉(图文详解)
不多说,直接上干货! 具体,见 Hadoop+Spark大数据巨量分析与机器学习整合开发实战的第13章 使用决策树二元分类算法来预测分类StumbleUpon数据集
- qq登录,新浪微博登录接口申请过程中遇到的问题
接口申请下来了,开发很容易的,参数传到就可以了.以前就做过这方面的开发,但是申请还是第一次,网上有关这方面的东东不是很多,所以记录一下申请过程. 1,qq登录接口申请 申请地址是:http://con ...
- xml和TreeView
1.TreeView代码代码: private void Form1_Load(object sender, EventArgs e) {<br> //代码为TreeView添加子项 tr ...
- promise从易到难
Chapter 1 // 需求:你要封装一个方法,我给你一个要读取文件的路径,你这个方法能帮我读取文件,并把内容返回给我 const fs = require('fs') const path = r ...
- jQuery deferred应用之ajax详细源码分析(二)
在上一节中,我只贴出了$.Deferred的源码分析,并没用讲解怎么使用它,现在我们先看看$.ajax是如何使用它,让我们进行异步任务的处理. 如果没看上节的代码,请先稍微了解一下jQuery Def ...
- (三)mybatis之对Hibernate初了解
前言:为什么会写Hibernate呢?因为HIbernate跟Mybatis一样,是以ORM模型为核心思想的,但是这两者有相似的地方也有差异的地方.通过这两种框架的比对,可以对mybatis有着更深的 ...
- Hibernate查询方式汇总
Hibernate总的来说共有三种查询方式:HQL.QBC和SQL三种.但是细分可以有如下几种: 一.HQL查询方式 这一种我最常用,也是最喜欢用的,因为它写起来灵活直观,而且与所熟悉的SQL的 ...
- iOS5 and iOS6都只支持横屏的方法
If your app uses a UINavigationController, then you should subclass it and set the class in IB. You ...
- jni 修bug
1. ReferenceTable overflow (max=512) 内存泄露,程序运行一段时间就挂掉了. 在利用反射调用java中的函数需要释放掉查找到的类 void publishJava ...