普里姆算法
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(Vertex (graph theory)),且其所有边的权值之和亦为最小。
基本介绍:
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
经典例题(修路问题):
问,现有7个地方A~G,需要把7个地方连通起来,每两个能连接的地方的距离(边)用权值来表示,A-B权值为5。在保证各个地方能连通,而且修建的路程最短为多少,怎么连通?
思路:
(1)修路问题实质就是最小生成树的问题,最小生成树是给定一个带权无向的连通图生成一棵树,使得树上的所以权加起来总和为最小。
最小生成树的特点:
<1>N个顶点,一定会有N-1条边
<2>此树包含全部顶点
<3>N-1条边都在图中
(2)若从A顶点开始处理,首先找到A能到达的点、没有访问过并且权值为最小的点。
如:<A,B>5,<A,C>7,<A,G>2,此时<A,G>2的权值2位最小,所以选择<A,G>此路径
(3)此时到达了G顶点,树为<A,G>从G顶点开始找,找到能到达的点、没有访问过并且权值为最小的点。如:<A,G,B>3 <A,G,E>4 <A,G,F>6,此时<A,G,B>的权值最小,所以选择<A,G,B>此路径
(4)以此类推(说白了就是每次循环查找的时候找到权值为最小并且没有访问过的边)
(5)直到找到<A,G,B,E,F,D>,把每次路径的权值打印出来。
实现代码:
package com.algorithm;
import java.util.Arrays;
public class PrimAlgorithm {
private static final int INF = Integer.MAX_VALUE;
public static void main(String[] args) {
char[] data =new char[]{'A','B','C','D','E','F','G'};
int vertxs = data.length;
int[][] weight = new int[][]{
{INF,5,7,INF,INF,INF,2},
{5,INF,INF,9,INF,INF,3},
{7,INF,INF,INF,8,INF,INF},
{INF,9,INF,INF,INF,4,INF},
{INF,INF,8,INF,INF,5,4},
{INF,INF,INF,4,5,INF,6},
{2,3,INF,INF,4,6,INF},
};
MapGraph mapGraph = new MapGraph(vertxs);
MinTree minTree = new MinTree();
minTree.createMapGraph(mapGraph,vertxs,data,weight);
minTree.showGraph(mapGraph);
minTree.primAlgorithm(mapGraph,1);
}
}
class MinTree
{
//创建邻接矩阵
//mapGrapg图,vertxs顶点数量,data顶点数据,weight邻接矩阵
public void createMapGraph(MapGraph mapGraph,int vertxs,char[] data,int[][] weight){
for (int i = 0; i < vertxs; i++) {
mapGraph.data[i] = data[i];//存放顶点数据
for (int j = 0; j < vertxs; j++) {//存放矩阵数据
mapGraph.weight[i][j] = weight[i][j];
}
}
}
//显示邻接矩阵
public void showGraph(MapGraph mapGraph)
{
for (int[] link:mapGraph.weight) {
System.out.println(Arrays.toString(link));
}
}
//创建普利姆算法,传入mapGraph图,从第v个顶点开始
public void primAlgorithm(MapGraph mapGraph,int v)
{
int count = 0;//声明count变量,用来计算累加每次的minWeight
int[] visted = new int[mapGraph.vertxs];//创建一个visited数组,当=1时为已经遍历过
visted[v] = 1;//初始化第v个顶点已经遍历过
int h1 = -1;//记录每个顶点是否遍历过
int h2 = -1;//记录每个顶点是否遍历过
int minWeight = 1000;//将权值初始为一个较大的值
for (int k = 1; k < mapGraph.vertxs; k++) {//从传入v的时候已经遍历过一次,所以k从1开始
//用双重循环来确定每次生成的图的最小的权值,确定每次生成和哪个结点的距离是最近的
for (int i = 0; i < mapGraph.vertxs; i++) {//i结点表示已经访问过的结点
for (int j = 0; j < mapGraph.vertxs; j++) {//j结点表示还没有访问的结点
//当i结点被访问过,j结点还没访问,而且i到j的结点权值比minWeight小,此时替换
if (visted[i] == 1 && visted[j] == 0 && mapGraph.weight[i][j] < minWeight) {
minWeight = mapGraph.weight[i][j];//重新赋minWeight的值
h1 = i;//记录i结点
h2 = j;//记录j结点
}
}
}
count += minWeight;
//输出每次的最小距离生成法
System.out.println("<" + mapGraph.data[h1] + "," + mapGraph.data[h2] +"> 权值:"+minWeight);
//每次遍历完就已经找到i->j结点的最小距离,此时需要重新赋值
minWeight = 1000;
visted[h2] = 1;//将j结点定义为已经访问过
}
System.out.println("最短路劲为:" + count);
}
}
class MapGraph
{
int vertxs;//定义图结点的数量
char[] data;//存放结点数据
int[][] weight;//存放邻接矩阵的边
//初始化邻接矩阵
public MapGraph(int vertxs) {
this.vertxs = vertxs;
data = new char[vertxs];
weight = new int[vertxs][vertxs];
}
}