1.Prim算法介绍
1.Prim算法是贪婪技术的一种。
2.贪婪技术:
在每一步中,都‘贪婪’地选择最佳的操作,并希望通过一些列局部最优选择,能够产生一个整个问题(全局)的最优解。
3.Prim算法的用处:
通俗的讲就是求解最小生成树问题。
4.Prim算法思想(贪婪的体现):
Prim算法将顶点分为2个集合vt和v-vt,并总是找出边(a,b)使得a∈vt,b∈v-vt,且边的权重最小。
5.Prim算法的关键之处:
Prim算法的关键之处在于求出边(a,b)使得a∈vt,b∈v-vt,且边的权重最小。此处求权重最小的边可以用优先队列PriorityQueuen来求取。
2.代码实现
package algorithm.experiment6;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Prim
{
//表示无穷大
final static Integer M = Integer.MAX_VALUE;
static int[][] G = {
{M,3,M,M,6,5},
{3,M,1,M,M,4},
{M,1,M,6,M,4},
{M,M,6,M,8,5},
{6,M,M,8,M,2},
{5,4,4,5,2,M}};
//全部顶点名称【对应数组的下标】
static char[] vName = {'a','b','c','d','e','f'};
//顶点是否被选【对应数组的下标】
static boolean[] vIsChoose = {true,false,false,false,false,false};
public static void main(String[] args)
{
prim(G);
}
public static void prim(int[][] G)
{
System.out.println("默认的点:a 集合vt:a");
while(true)
{
//判断是否生成了最小生成树
if(isOver()) break;
PriorityQueue<MyEdge> myPriorityQueue = myPriorityQueue(G,vIsChoose);
MyEdge edge = myPriorityQueue.poll();
changeG(edge);
// System.out.print(edge+" ");
System.out.print("选择的点:"+vName[edge.index2]+" 集合vt:");
for(int i=0;i<vIsChoose.length;i++)
if(vIsChoose[i])
System.out.print(vName[i]+" ");
System.out.println();
}
}
//判断是否找到了最小生成树(判断数组vIsChoose如果全为true则返回true)
public static boolean isOver()
{
for(Boolean b:vIsChoose){
if(!b) return false;
}
return true;
}
//改变图以及已经选择的标记数组
private static void changeG(MyEdge edge)
{
int x = edge.index1;
int y = edge.index2;
G[x][y] = M;
G[y][x] = M;
vIsChoose[x] = true;
vIsChoose[y] = true;
}
//构造一个myEdge的优先队列
public static PriorityQueue<MyEdge> myPriorityQueue(int[][] G,boolean[] vIsChoose)
{
PriorityQueue<MyEdge> myPriorityQueue = new PriorityQueue<MyEdge>(11,myEdgeComparator());
for(int i=0;i<vIsChoose.length;i++)
{
//如果顶点被选则找出相邻的顶点并组成边加入到优先队列中
if(vIsChoose[i])
{
//遍历相应的顶点
for(int j=0;j<G[i].length;j++)
{
//将相邻的顶点组成的边加入优先队列 且该边的连个顶点是包含于vt和v-vt的
if(G[i][j]!=M&&!vIsChoose[j])
{
MyEdge edge = new MyEdge(i, j, G[i][j]);
myPriorityQueue.offer(edge);
}
}
}
}
return myPriorityQueue;
}
//表示边 index1:表示顶点1的在字符数组中的下标
static class MyEdge
{
public int index1,index2;
public int weight;
public MyEdge(int v1,int v2,int weight){
this.index1 = v1;
this.index2 = v2;
this.weight = weight;
}
public String toString(){
return "("+vName[index1]+","+vName[index2]+","+weight+")";
}
}
//myEdge比较器
public static Comparator<MyEdge> myEdgeComparator()
{
Comparator<MyEdge> comparator = new Comparator<MyEdge>(){
@Override
public int compare(MyEdge o1, MyEdge o2)
{
return o1.weight-o2.weight;
}
};
return comparator;
}
}
3.运行结果
默认的点:a 集合vt:a
选择的点:b 集合vt:a b
选择的点:c 集合vt:a b c
选择的点:f 集合vt:a b c f
选择的点:e 集合vt:a b c e f
选择的点:d 集合vt:a b c d e f
4.代码分析
首先是图的表示,我采用的是邻接矩阵的方式来表示图,用vName的字符数组表示顶点名称,用vIsChoose表示相应的坐标是否被选择。
内部类MyEdge用于表示边,在myPriorityQueue(int[][] G,boolean[] vIsChoose)方法中首先构建了一个优先队列,该队列通过自己实现的比较器(方法myEdgeComparator()实现)每次出队都可以得到一个满足条件的边(a,b)(权重最小,a∈vt,b∈v-vt)。依次循环直到找到最小生成树从而退出循环。