一、什么是普鲁姆算法?
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小
二、原理
1、从出发节点开始只能收集出发点和其相临节点的权值,选取收集的权值中最小权值(已被访问节点不在选择范围内),该权值指向节点作为下一个出发点,标记该指向节点已被访问
2、重复步骤1直至所有节点全部被访问
三、代码实现
/**
* 最小生成树
* 普里姆算法
*/
public class Test {
/**
* 端点数组
*/
private static final String[] ENDPOINT;
/**
* 指向节点的权值数组,下标是尾节点
*/
private static final int[] WEIGHT_SIDE;
/**
* 指向某节点最短路径数组,下标尾节点 值为首节点,若值为-1说明该尾节点已经被访问过
*/
private static final int[] SHORT_PATH_ENDPOINT;
/**
* 边二维数组
*/
private static final Integer[][] SIDE_ARRAY;
/**
* 判断顶点是否被访问过
*/
private static final Integer SIDE_FLAG = -1;
static {
ENDPOINT = new String[]{"V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8"};
WEIGHT_SIDE = new int[ENDPOINT.length];
SHORT_PATH_ENDPOINT = new int[ENDPOINT.length];
for (int i = 0; i < WEIGHT_SIDE.length; i++) {
// 因为节点有为0的因此初始化数组是用Integer.MAX_VALUE
WEIGHT_SIDE[i] = Integer.MAX_VALUE;
SHORT_PATH_ENDPOINT[i] = Integer.MAX_VALUE;
}
SIDE_ARRAY = new Integer[ENDPOINT.length][ENDPOINT.length];
for (int i = 0; i < ENDPOINT.length; i++) {
for (int j = 0; j < ENDPOINT.length; j++) {
SIDE_ARRAY[i][j] = Integer.MAX_VALUE;
}
}
SIDE_ARRAY[0][0] = 0;
SIDE_ARRAY[1][1] = 0;
SIDE_ARRAY[2][2] = 0;
SIDE_ARRAY[3][3] = 0;
SIDE_ARRAY[4][4] = 0;
SIDE_ARRAY[5][5] = 0;
SIDE_ARRAY[6][6] = 0;
SIDE_ARRAY[7][7] = 0;
SIDE_ARRAY[8][8] = 0;
SIDE_ARRAY[0][1] = 10;
SIDE_ARRAY[1][0] = 10;
SIDE_ARRAY[0][5] = 11;
SIDE_ARRAY[5][0] = 11;
SIDE_ARRAY[1][2] = 18;
SIDE_ARRAY[2][1] = 18;
SIDE_ARRAY[1][8] = 12;
SIDE_ARRAY[8][1] = 12;
SIDE_ARRAY[1][6] = 16;
SIDE_ARRAY[6][1] = 16;
SIDE_ARRAY[2][8] = 8;
SIDE_ARRAY[8][2] = 8;
SIDE_ARRAY[2][3] = 22;
SIDE_ARRAY[3][2] = 22;
SIDE_ARRAY[3][8] = 21;
SIDE_ARRAY[8][3] = 21;
SIDE_ARRAY[3][6] = 24;
SIDE_ARRAY[6][3] = 24;
SIDE_ARRAY[3][7] = 16;
SIDE_ARRAY[7][3] = 16;
SIDE_ARRAY[3][4] = 20;
SIDE_ARRAY[4][3] = 20;
SIDE_ARRAY[4][7] = 7;
SIDE_ARRAY[7][4] = 7;
SIDE_ARRAY[4][5] = 26;
SIDE_ARRAY[5][4] = 26;
SIDE_ARRAY[5][6] = 17;
SIDE_ARRAY[6][5] = 17;
SIDE_ARRAY[6][7] = 19;
SIDE_ARRAY[7][6] = 19;
}
public static void main(String[] args) {
//随机选择起点、终点
Random random = new Random();
int startEndPoint = random.nextInt(ENDPOINT.length - 1);
String vertex = ENDPOINT[startEndPoint];
System.out.println("开始最小生成树!开始顶点:" + vertex);
SHORT_PATH_ENDPOINT[startEndPoint] = -1;
weightedShortestPath(startEndPoint);
}
/**
* 获取普里姆算法加权最小生成树
* 时间复杂度n的平方
*
* @param index
*/
private static void weightedShortestPath(int index) {
for (int i = 0; i < ENDPOINT.length; i++) {
int minWeight = Integer.MAX_VALUE;
int minEndpoint = -1;
// 1、遍历当前节点的邻节点 取最小权值 指向的节点下标
for (int j = 0; j < ENDPOINT.length; j++) {
int weight = SIDE_ARRAY[index][j];
// weight == Integer.MAX_VALUE 表示 俩顶点无边 ;index == j表示矩阵对角线 ;SHORT_PATH_ENDPOINT == SIDE_FLAG表示j端点已经被访问过
if (weight == Integer.MAX_VALUE || index == j || SHORT_PATH_ENDPOINT[j] == SIDE_FLAG) {
continue;
}
// 获取 当前index节点的邻节点最小权值路径
if (weight < minWeight) {
minWeight = weight;
minEndpoint = j;
}
if (weight < WEIGHT_SIDE[j]) {
WEIGHT_SIDE[j] = weight;
SHORT_PATH_ENDPOINT[j] = index;
}
}
// 将1步骤获取的最小权值指向下标,和之前剩余可用权值比较谁最小,取最小权值下标
for (int k = 0; k < WEIGHT_SIDE.length; k++) {
// SHORT_PATH_ENDPOINT[k] == Integer.MAX_VALUE 表示 无最短路径;SHORT_PATH_ENDPOINT[k] == SIDE_FLAG表示端点k已经被访问过
if (SHORT_PATH_ENDPOINT[k] == Integer.MAX_VALUE || SHORT_PATH_ENDPOINT[k] == SIDE_FLAG) {
continue;
}
int weight = WEIGHT_SIDE[k];
// minEndpoint == -1说明index节点周围没有未访问过的节点
if (minEndpoint == -1) {
minEndpoint = k;
}
if (weight < WEIGHT_SIDE[minEndpoint]) {
minEndpoint = k;
}
}
// 说明已经遍历完成
if (minEndpoint == -1) {
continue;
}
System.out.println("路径:" + SHORT_PATH_ENDPOINT[minEndpoint] + "-" + minEndpoint + ",权值:" + WEIGHT_SIDE[minEndpoint]);
// 标记最小权值指向节点已被访问
SHORT_PATH_ENDPOINT[minEndpoint] = SIDE_FLAG;
// 将最小权值指向节点作为下一步出发点
index = minEndpoint;
}
}
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了普鲁姆算法的原理及代码实现。