最小生成树算法Prim、Kruskal

来源:互联网 发布:趣头条刷金币软件 编辑:程序博客网 时间:2024/05/18 09:52
在看本文之前最好先看看算法分析之最小生成树
在介绍两种算法之前,我需要先介绍一个推论
首先介绍一个概念,连通分量:其实就是一棵树(某些树也可能是一个节点)

推论:对于无向连通图G=(V,E)。设A是G的某棵最小生成树的子集,并设C=(Vc,Ec)为森林Ga=(V,A)的一个连通分量,如果边(u,v)是连接C和Ga中某个其他连通分量的一条轻量级边,则(u,v)对于A来说是安全的。


Kruskal算法
在所有的连接两颗树的边里找到权重最小的边(u,v),设C1,C2分别为u,v所连接的两个连通分量由于(u,v)一定是连接两个连通分量C1,C2的轻量级边,由推论可知(u,v)是安全的
算法伪代码:
A=null
对G中的边E按权重w对每条边安卓小到大的顺序进行排序

for each (u,v)∈G.E{
如果u,v不属于同一颗树{
A=A∪{(u,v)}
将u,v所在的树合并成一棵树
}
}
具体过程可以用下图来说明


算法模板:

public class Mian{public static void main(String[] args){/** * 对fatherV初始化 **/for (int i = 0; i < fatherV.length; i++){fatherV[i]=i;}/** * 给e赋值 **///TODO/** * 给V赋值 **///TODOkruskal();}static int V;//结点的个数static int[] fatherV=new int[100];//farher[i]=j表示结点i的父节点为jstatic Edge[] e=new Edge[100];static class Edge{int u,v,w;Edge(int u,int v,int w){this.u=u;this.v=v;this.w=w;}}/** * 寻找结点x所在树的根节点 **/static int findRoot(int x){return (x==fatherV[x]) ? x : findRoot(fatherV[x]);}/** * 判断结点u,v是否属于同一棵树,如果不属于同一棵树就将u,v所在的树合并为一棵树 **/static boolean judgeIsSameTree(int u,int v){int root1=findRoot(u);int root2=findRoot(v);if(root1==root2)//u,v在同一棵树中{return true;}else {fatherV[root1]=root2;//将u,v所在的两颗树合并}return false;}static void kruskal(){boolean flag=false;int numEgdeInTree=0;quickSort(0, e.length);for(int i=0;i<e.length;i++){if(!judgeIsSameTree(e[i].u, e[i].v)){numEgdeInTree++;if(numEgdeInTree==V-1)//找到了一棵最小生成树{flag=true;break;}}}if(flag){//TODO}else {System.out.println("can not find a minimal spanning tree");}}/** * 快排对e按w进行升序排序,这里排序可以随意选择方法,也可以用语言给定好的排序函数 **/static void quickSort(int l,int r){if(l>=r){return;}int q=partition(l, r);quickSort(l, q-1);quickSort(q+1, r);}static int partition(int l,int r){int i=l;int j=r;int x=e[i].w;while(i<j){while(i<j && e[j].w>=x) j--;if(i<j){e[i].w=e[j].w;i++;}else {break;}while(i<j && e[i].w<=x) i++;if(i<j){e[j].w=e[i].w;j--;}else {break;}}//显然退出时i=je[i].w=x;return i;}}

Prim算法
算法的每一步都寻找一条横跨切割(A,V-A)的轻量级边作为A的安全边
设v.key为v到A中每个结点的边中最小边的权重
伪代码:
A=null
Q=G.V
任意确定一个点作为root加入到A中
while(Q!=null){
从Q找出结点u,u.key是Q中的所有结点中最小的
将u加入到A并从Q中删除
更新与u相连的结点的v.key
}

用下面的一系列图来说明:


prim模板如下:
public class Main{public static void main(String[] args){prim();}static int[] low=new int[100];//low[i]=d表示结点i到A中的结点的最小边的权值为dstatic int used[]=new int[100];//used[i]=1,0分别表示i结点在集合A中和i结点不在集合A中static int n;//图中结点的数目static int[][] w=new int[100][100];//w[i][j]结点i到j的边的权值static void prim(){//初始化所有结点的low值全部设为无限大for(int i=0;i<low.length;i++){low[i]=2147483647;}//以结点0为初始结点,标记该结点int pos=0;used[pos]=1;for(int i=1;i<n;i++){low[i]=w[pos][i];}for(int i=1;i<n;i++){/** * 找出集合A到A以外的结点的边的权值最小的结点 **/int min_low=1000000;for(int j=1;j<n;j++){if(used[j]==0 && low[j]<min_low){min_low=low[j];pos=j;}}/** * 将找到的结点加入到集合A,并更新与该结点相连的结点的low值 **/used[pos]=1;for(int j=1;j<n;j++){if(used[j]==0 && w[pos][j]<low[j]){low[j]=w[pos][j];//TODO,结点j的父节点为pos}}}}}


0 0
原创粉丝点击