生成树
对连通图进行遍历,过程中所经过的边和顶点的组合可看做是一棵普通树,通常称为生成树。
对非连通图进行遍历,过程中所经过的边和顶点的组合可看做是一个森林,通常称为生成森林。
最小生成树
由生成树的定义可知,无向连通图的生成树不是唯一的。
连通图的一次遍历所经过的边的集合及图中的所有顶点的集合就构成了该图的一颗生成树,
对连通图的不同遍历,如遍历出发点不同或储存点的顺序不同,就可能得到不同的生成树。
对于有n个顶点的无向连通图,无论其生成树的形态如何,所有生成树中都有且仅有n-1条边。
如果无向连通图是一个带权图,那么,它的所有生成树中必有一颗边的权值总和最小的生成树,称其为最小生成树
最小生成树的实现:Prim(普里姆)算法
假设G=(V,E)是一个网图,其中V是所有顶点的集合,E是所有带权边的集合。
设置两个新的集合U和T,U用于存放G的最小生成树的顶点,T用于存放G的最小生成树的边。
令U的初值为出发点,T的初值为空,Prim的思想就是在U集合中所有顶点u和V-U集合中所有顶点v选取最小权值的边(u,v)
然后将顶点v加入U中,将(u,v)加入T中,如此不断重复,直到U=V时,最小生成树构造完毕
算法思路:
U={u},T={};
while(U!=V){
(u,v)=min(uv的最小权值的边|u属于U,v属于(V-U));
T=T+{(u,v)};
U=U+{v};
}
代码实现:
public void Prim( Graph G,int tree[],int cost[]){
int flag[]=new int[10];
int k = 0,mincost;
for (int i = 1; i < G.Vnum; i++) {
cost[i]=G.edges[0][i];
tree[i]=0;
}
flag[0]=1;
tree[0]=-1;
for (int i = 1; i < G.Vnum; i++) {
mincost=10000;
for (int j = 1; j < G.Vnum; j++) {
if (flag[j]==0&&cost[j]<mincost) {
mincost=cost[j];
k=j;
}
}
flag[k]=1;
for (int j = 1; j < G.Vnum; j++) {
if (flag[j]==0&&G.edges[k][j]<cost[j]) {
cost[j]=G.edges[k][j];
tree[j]=k;
}
}
}
}
}
最小生成树的实现:Kruskal(克鲁斯卡尔)算法
克鲁斯卡尔算法是一种按照网中边的权值递增的顺序构造最小生成树的方法,其基本思想是:
设无向连通网G=(V,E),令G的最小生成树为T=(Vt,Et),其初值为Vt=V,Et=空,即把所有的顶点加入进去,所有的边不加入。
然后从E-Et中选取权值最小的边,若该边的两个顶点属于T中的两个不同的连通子图,加入该边不构成回路,
则将此边作为最小生成树的边加入到Et中,若加入进去构成回路,则舍弃此边,选取下一条权值较小的边,
如此选取n-1次之后,此时连通子图即为G的最小生成树
代码实现:
public void Kruskal(Graph G,Edge edges[],Edge T[]){
int father[]=new int[100];
for (int i : father) {
father[i]=-1;
}
int i=0,j=0;
int vf1,vf2;
while(i<G.Enum&&j<G.Vnum-1){
vf1=Find(father, edges[i].v1);
vf2=Find(father, edges[i].v2);
if(vf1!=vf2){
father[vf2]=vf1;
T[j]=edges[i];
j++;
}
i++;
}
}
public int Find(int father[],int v){
int t=v;
while(father[t]>=0) t=father[t];
return t;
}