啦啦啦~~~笔者又回来了。今天给诸位带来的是这段时间笔者在coursera上面跟着北大的老师学离散的时候学到的最小生成树里面的一种叫Kruskal算法,除此以外还有Prim算法(但是笔者还没有弄懂)
首先理清几个离散数学上面的几个概念
①连通图:在无向图中,若任意两个点和都有路径相通,则称该无向图为连通图
②赋权图:每条边都有一个非负实数对应的图。这个实数称为这条边的权。
③生成子图:设G=<V,E>,G’=<V’,E’>是两个图,若V’V,且E’E,则称G’是G的子图,G是G’的母图,记作G’G.若G’G且V’=V,则称G’是G的生成子图
④生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,且又n-1条边构成一棵树
⑤最小生成树:在赋权图中,使得最终权值最小的生成树被称为最小生成树
接下来介绍Kruskal算法的中心思想
Kruskal算法的中心思想就是每次取权尽可能小的边,只要它与已取的边不构成回路,直到取得一棵生成树为止,这也叫避圈法。
然后就是介绍Kruskal算法的操作步骤:
输入:n阶无向连通带权图G=<V,E,W>,其中E=<e1,e2,e3,....em>
输出:G的最小生成树T
(1)按权从小到大排列边,不妨设W(e1)W(e2)....W(em)
(2)令T←Ø,i←1,k←0.
(3)若ei与T中的边不构成回路,则令T←T{e1},k←k+1/若条件成立,则取ei,否则弃去ei
(4)若k<n-1,则令i←i+1,转(3).
如图就是一个最小生成树的示例
下面是代码展示
这个算法的主要难点就是如何去判断回路
在参考了其他博主的代码后,发现有很多是应用了指针来实现的,如下
int Find(int *parent,int f)
{
while(parnet[f]>0) f = parent[f]
return f;
}
下面就是完整的代码展示
int Find(int *parent,int f)
{
while(parnet[f]>0)
f = parent[f]
return f;
}
Typedef struct
{
int begin;
int end;
int weight;
}Edge;
void Kruskal(MGraph g)
{
Edge E[MaxSize];
int vset[MaxSize];
int i,k,j;
for(i=0;i<g.n;i++)
for(j=0;j<g.n;j++)
if(g.edges[i][j]!=0 && g.edges[i][j]!=INF)
{
E[k].begin = i;
E[k].end = j;
E[k].weight = g.edges[i][j];
k++;
}
InsertSort(E,g.e);//排序
for(i=0;i<g.n;i++) vset[i]=0;
//要区n-1条边出来
k = 1;
//每次取出一条边,所以要有一个元素记住现在取到第几条边了
j = 0;
while(k<g.n)
{
u1 = E[j].begin;
u2 = E[j].end;
sn1 = Find(vset,u1);
sn2 = Find(vset,u2);
if(sn1!=sn2)
{
vset[sn2] = sn1;
printf();
k++;
}
j++;
}
}
P.S.文章中所有离散数学概念来自清华大学出版社屈婉玲等人编著的《离散数学》第五版,代码来自博客http://blog.csdn.net/qq122627018/article/details/51912324