Prim算法
普里姆(Prim)算法,是用来求加权连通图的最小生成树的算法。
基本思想 :
对于图G而言,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最小生成树中的边。 从所有uЄU,vЄ(V-U) (V-U表示出去U的所有顶点)的边中选取权值最小的边(u, v),将顶点v加入集合U中,将边(u, v)加入集合T中,如此不断重复,直到U=V为止,最小生成树构造完毕,这时集合T中包含了最小生成树中的所有边。
伪代码:
图解:
以上图G4为例,来对普里姆进行演示(从第一个顶点A开始通过普里姆算法生成最小生成树)。
初始状态:V是所有顶点的集合,即V={A,B,C,D,E,F,G};U和T都是空!
第1步:将顶点A加入到U中。
此时,U={A}。
第2步:将顶点B加入到U中。
上一步操作之后,U={A}, V-U={B,C,D,E,F,G};因此,边(A,B)的权值最小。将顶点B添加到U中;此时,U={A,B}。
第3步:将顶点F加入到U中。
上一步操作之后,U={A,B}, V-U={C,D,E,F,G};因此,边(B,F)的权值最小。将顶点F添加到U中;此时,U={A,B,F}。
第4步:将顶点E加入到U中。
上一步操作之后,U={A,B,F}, V-U={C,D,E,G};因此,边(F,E)的权值最小。将顶点E添加到U中;此时,U={A,B,F,E}。
第5步:将顶点D加入到U中。
上一步操作之后,U={A,B,F,E}, V-U={C,D,G};因此,边(E,D)的权值最小。将顶点D添加到U中;此时,U={A,B,F,E,D}。
第6步:将顶点C加入到U中。
上一步操作之后,U={A,B,F,E,D}, V-U={C,G};因此,边(D,C)的权值最小。将顶点C添加到U中;此时,U={A,B,F,E,D,C}。
第7步:将顶点G加入到U中。
上一步操作之后,U={A,B,F,E,D,C}, V-U={G};因此,边(F,G)的权值最小。将顶点G添加到U中;此时,U=V。
此时,最小生成树构造完成!它包括的顶点依次是:A B F E D C G。
public class Prim {
/** 邻接矩阵, 为二维数组 */
int[][] matrix;
/** 设置最大权重范围, 表示正无穷 */
int MAX_WEIGHT = Integer.MAX_VALUE;
/** 顶点个数 */
int size;
/**
* 普里姆算法:先初始化拿到第一个顶点相关联的权值元素放到数组中-> 找到其中权值最小的顶点下标-》再根据该下标,将该下标顶点相关联的权值加入到数组中-》循环遍历处理
*/
public void prim() {
/** 存放当前到全部顶点最小权值的数组,如果已经遍历过的顶点权值为0,无法到达的为正无穷 */
int[] tempWeight = new int[size];
int minWeight; /** 当前到下一个最小权值顶点的最小权值 */
int minId; /** 当前到下一个最小权值的顶点 */
int sum = 0; /** 权值总和 */
//初始化"顶点的权值数组", 第一个顶点时, 到其他顶点的权值即为邻接矩阵的第一行
for (int i = 0; i < size; i++) {
//将与v0顶点有边的权值存入数组
tempWeight[i] = matrix[0][i];
}
System.out.println("从顶点v0开始查找");
//最外层循环开始
for (int i = 1; i < size; i++) {
//每次循环找出当前到下一个最小权值的顶点极其最小权值
minWeight = MAX_WEIGHT;
minId = 0;
for (int j = 1; j < size; j++) {
//如果权值不为0 且权值小于 minWeight
if (tempWeight[j] > 0 && tempWeight[j] < minWeight) {
//让当前权值为最小值
minWeight = tempWeight[j];
//将当前最小值的下标存入minId
minId = j;
}
}
//找到目标顶点minId,他的权值为minWeight。
System.out.println("找到顶点:v" + minId + " 权值为:" + minWeight);
sum += minWeight; //加上对应边的权重,更新sum值
/**
* 算法核心所在:将目标顶点到各个顶点的权值与当前tempWeight数组中的权值做比较,
* 如果前者比后者到某个顶点的权值更小,
* 将前者到这个顶点的权值更新入后者。
*/
//将"第minId个顶点的权值"标记为0,意味着这顶点已经排序过了(或者说已经加入了最小树结果中)
tempWeight[minId] = 0;
for (int j = 1; j < size; j++) {
//若下标为minId 的顶点各边权值 小于 此前这些顶点未被加入生成树权值
if (tempWeight[j] != 0 && matrix[minId][j] < tempWeight[j]) {
//将较小权值存入tempWeight
tempWeight[j] = matrix[minId][j];
}
}
}
//最外层循环结束
System.out.println("最小权值总和为:" + sum);
}
private void createGraph(int index) { //index表示顶点个数
size = index;
matrix = new int[index][index]; //邻接矩阵是一个长宽相等的正方形矩阵
//邻接矩阵中每一行的数组, 按照原图以此填充
int[] v0 = { 0, 10, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
int[] v1 = { 10, 0, 18, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12 };
int[] v2 = { MAX_WEIGHT, 18, 0, 22, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8 };
int[] v3 = { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, MAX_WEIGHT, 16, 21 };
int[] v4 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20, 0, 26, MAX_WEIGHT, 7, MAX_WEIGHT };
int[] v5 = { 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26, 0, 17, MAX_WEIGHT, MAX_WEIGHT };
int[] v6 = { MAX_WEIGHT, 16, MAX_WEIGHT, 24, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
int[] v7 = { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, 7, MAX_WEIGHT, 19, 0, MAX_WEIGHT };
int[] v8 = { MAX_WEIGHT, 12, 8, 21, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0 };
matrix[0] = v0;
matrix[1] = v1;
matrix[2] = v2;
matrix[3] = v3;
matrix[4] = v4;
matrix[5] = v5;
matrix[6] = v6;
matrix[7] = v7;
matrix[8] = v8;
}
public static void main(String[] args) {
Prim graph = new Prim();
graph.createGraph(9);
graph.prim();
}
}
结果:
代码中所用的图示:
最小生成树:99