普利姆算法之最短路径问题详解
说明
- 普利姆算法是一个求最短路径的算法,即给定一个带权的无向图,求一条路径使得将这些节点连接后带权路径最短,即如何生成最小生成树
- 以修路问题为例,假设有7个村庄,修一条通路连接这7个村庄,但是要求路径最短
- 使用无向图来模拟,图的顶点为村庄,带权路径为村庄的通路,则转化为求图的最小权值问题
- 使用邻接矩阵来表示图
- 将邻接矩阵创建好后,实现普利姆算法,思路???
- 仍然创建一个大小等于节点数目的一维数组表示当前节点是否访问过,如果访问过,则置1,如果没有访问过则置0
- 从某一节点开始,先将当前节点置为1,表示访问过
- 因为有7个节点,所有至少需要6条通路才能连接所有的顶点,所以最外层循环总共循环6次,每次循环都寻找一条最短路径
- 内层循环则要在已经访问过的和没有访问过的节点连接中寻找最短路径,找到后记录两个节点并记录权值
- 通过6次大循环后则会找到6条路径将所有的节点连接起来并能保证路径最短
- 源码见下
源码及分析
package algorithm.algorithm.prim;
import java.util.Arrays;
/**
* @author AIMX_INFO
* @version 1.0
*/
public class PrimAlgorithm {
public static void main(String[] args) {
//顶点元素
char[] data = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
//顶点个数
int vertex = data.length;
//设置最小生成树的权值
int[][] weight = {{10000, 5, 7, 10000, 10000, 10000, 2},
{5, 10000, 10000, 9, 10000, 10000, 3},
{7, 10000, 10000, 10000, 8, 10000, 10000},
{10000, 9, 10000, 10000, 10000, 4, 10000},
{10000, 10000, 8, 10000, 10000, 5, 4},
{10000, 10000, 10000, 4, 5, 10000, 6},
{2, 3, 10000, 10000, 4, 6, 10000}
};
//图对象
MGraph graph = new MGraph(vertex);
//最小生成树
MinTree minTree = new MinTree();
//邻接矩阵
minTree.createGraph(graph, vertex, data, weight);
//minTree.showGraph(graph);
//普利姆算法
minTree.prim(graph,3);
}
}
//创建最小生成树
class MinTree {
//创建图的邻接矩阵
/**
* @param graph 图
* @param vertex 顶点个数
* @param data 顶点的数据
* @param weight 边的权值
*/
public void createGraph(MGraph graph, int vertex, char[] data, int[][] weight) {
for (int i = 0; i < vertex; i++) {
//给每个顶点赋值
graph.data[i] = data[i];
//给每条路径赋权值
for (int j = 0; j < vertex; j++) {
graph.weight[i][j] = weight[i][j];
}
}
}
/**
*
* @param graph 最小生成树
* @param v 从哪个节点开始
*/
public void prim(MGraph graph, int v){
//创建一维数组判断是否访问过,如果访问过,则置1,否则置为0
int[] isVisited = new int[graph.vertex];
//从当前节点开始,则当前节点已经被访问过
//定义变量h1 h2保存最小路径的两个顶点下标
int h1 = -1, h2 = -1;
//定义minWeight保存最小路径
int minWeight = 10000;
isVisited[v] = 1;
//因为有graph.vertex个顶点,因此至少需要graph.vertex - 1条线连接所有的顶点
for (int k = 1; k < graph.vertex; k++) {
//寻找 已经访问过的节点和其他未访问过的邻接节点之间的最小路径
for (int i = 0; i < graph.vertex; i++) {
for (int j = 0; j < graph.vertex; j++) {
//i节点访问过,j节点没有访问过并且两节点之间的路径最短,则记录权值和两个节点
if (isVisited[i] == 1 && isVisited[j] == 0 && graph.weight[i][j] < minWeight){
minWeight = graph.weight[i][j];
h1 = i;
h2 = j;
}
}
}
//内两层循环结束后则找到一条最短路径
System.out.println("边 " + graph.data[h1] + " " + graph.data[h2] + " 权值为 " + graph.weight[h1][h2]);
//将当前节点标记为已经访问
isVisited[h2] = 1;
//重置权值
minWeight = 10000;
}
}
//查看最小生成树
public void showGraph(MGraph graph) {
for (int[] link : graph.weight) {
System.out.println(Arrays.toString(link));
}
}
}
//图类
class MGraph {
//节点个数
int vertex;
//节点数据
char[] data;
//节点的边,即邻接矩阵
int[][] weight;
//构造器,通过节点个数创建图
public MGraph(int vertex) {
this.vertex = vertex;
data = new char[vertex];
weight = new int[vertex][vertex];
}
}