前言需要
明天咱们学习的是普里姆算法,咱们还是从一个场景里引入看看
有7个村庄(A, B, C, D, E, F, G) ,当初须要修路把7个村庄连通
1.各个村庄的间隔用边线示意(权)
,比方 A – B 间隔 5公里
2.问如何修路保障各个村庄都能连通,并且总的建筑公路总里程最短
?
咱们的思路就是尽可能的抉择少的路线,并且每条路线最小,保障总里程数起码
一、什么是普里姆算法
普利姆(Prim)算法求:最小生成树就是在蕴含n个顶点的连通图
中,找出只有(n-1)条边蕴含所有n个顶点的连通子图
,也就是所谓的极小连通子图
二、通过示例意识普里姆算法
其实修路的问题实质就是最小生成树的问题
先介绍一下最小生成树(Minimum Cost Spanning Tree),简称MST
那么什么是最小生成树呢?其实就是给定一个带权的无向连通图
,如何选取一棵生成树,使树上所有边上权的总和为最小
。这叫最小生成树
那么最小生成树有什么特点呢?
1.若有N个顶点,肯定要至多有N-1条边
2.图要蕴含全副顶点
3.N-1条边要都在图中
那么求最小生成树的算法次要有普里姆算法和克鲁斯卡尔算法
普里姆算法思路剖析
1.设G=(V,E)是连通网,T=(U,D)是最小生成树,V,U是顶点汇合,E,D是边的汇合
2.若从顶点u开始结构最小生成树,则从汇合V中取出顶点u放入汇合U中,标记顶点v的visited[u]=1
3.若汇合U中顶点ui与汇合V-U中的顶点vj之间存在边,则寻找这些边中权值最小的边,但不能形成回路。
将顶点vj退出汇合U中,将边(ui,vj)退出汇合D中,标记visited[vj]=1
4.反复步骤②,直到U与V相等,即所有顶点都被标记为拜访过,此时D中有n-1条边
普里姆算法图解思路剖析
1.如果从A顶点开始解决,将A放入汇合中,并可连贯到其余顶点有C、G、B
2.此时这三条边的最小权重值是A-G 为2,于是咱们G放入汇合中,《A,G》
3.此时这五条边的最小权重值是B-B 为3,于是咱们B放入汇合中,《A,G,B》
4.此时这四条边的最小权重值是G-E 为4,于是咱们E放入汇合中,《A,G,B,E》
此时这五条边的最小权重值是E-F 为5,以此类推退出到汇合中....
最初的汇合后果应该是<A,G,B,E,F,D,C>
普里姆代码思路剖析
1.咱们须要一个寄存顶点的char数组,以及顶点的边数
2.咱们须要应用邻接矩阵来示意顶点之间的连贯状况与权重值
3.咱们须要生成minTree树
4.咱们须要创立图
普里姆算法代码实际
1.应用邻接矩阵来示意图所之间连贯关系与权重值
//应用邻接矩阵形容权重值示意0 代表无int[][] weight = new int[][]{ {0,5,7,0,0,0,2}, {5,0,0,9,0,0,3}, {7,0,0,0,8,0,0}, {0,9,0,0,0,4,0}, {0,0,8,0,0,5,4}, {0,0,0,4,5,0,6}, {2,3,0,0,4,6,0}};
2.须要一个寄存顶点的char数组,以及顶点的边数
//char[] 数组寄存顶点个数char[] data = new char[]{'A','B','C','D','E','F','G'};int verxs = data.length;
3.创立图对象
class MGraph{ int verxs;//示意图的节点数 char[] data;//寄存结点数据 int[][] weight;//寄存边 public MGraph(int verxs) { this.verxs = verxs; data = new char[verxs]; weight = new int[verxs][verxs]; }}
4.咱们须要将创立的这些值与数组、赋值给到图对象,并创立输入图的办法
class MinTree{ //创立图的邻接矩阵 public void createGraph(MGraph graph,int verxs,char[] data,int[][] weight){ for ( int i = 0 ; i<verxs;i++){ //将char数组里的值赋值给图 graph.data[i] = data[i]; for (int j = 0; j<verxs;j++ ){ //将weight里的值赋值给图 graph.weight[i][j] = weight[i][j]; } } } //打印图的信息 public void showGraph(MGraph graph){ for ( int[] link : graph.weight){ System.out.println(Arrays.toString(link)); } }}
接下来咱们应用demo 实现图的创立与输入
public static void main(String[] args) { //char[] 数组寄存顶点个数 char[] data = new char[]{'A','B','C','D','E','F','G'}; //顶点之间的边 int verxs = data.length; //应用邻接矩阵形容权重值示意0 代表无 int[][] weight = new int[][]{ {0,5,7,0,0,0,2}, {5,0,0,9,0,0,3}, {7,0,0,0,8,0,0}, {0,9,0,0,0,4,0}, {0,0,8,0,0,5,4}, {0,0,0,4,5,0,6}, {2,3,0,0,4,6,0} }; MGraph graph = new MGraph(verxs); MinTree minTree = new MinTree(); minTree.createGraph(graph,verxs,data,weight); minTree.showGraph(graph);}
依据普里姆算法思路编写算法代码
1.咱们的思路是先找到一个顶点开始,一直找相邻顶点,比如说A开始
找相邻顶点的条件是:A已拜访,其余顶点为未拜访
2.判断A-其余顶点的weight,是否是最小的
为了不便找到最小的,咱们这里应用一个很大的数,来代替刚刚的:0
public void prim(MGraph graph,int v){ //应用数组标注节点是否被拜访过 int[] visited = new int[graph.verxs]; for ( int i=0; i<visited.length; i++){ visited[i] = 0; } //将以后节点标注为已拜访 visited[v] = 1; int h1 = -1; int h2 = -1; int minWeight = 10000; for(int k = 1; k < graph.verxs; k++) { for(int i = 0; i < graph.verxs; i++) { for(int j = 0; j< graph.verxs;j++) { if(visited[i] == 1 && visited[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] + "> 权值:" + minWeight); visited[h2] = 1; minWeight = 10000; } }
行将上图所至的0 --> 10000
public static void main(String[] args) { //char[] 数组寄存顶点个数 char[] data = new char[]{'A','B','C','D','E','F','G'}; //顶点之间的边 int verxs = data.length; //应用邻接矩阵形容权重值示意0 代表无 int[][] weight = new int[][]{ {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(verxs); MinTree minTree = new MinTree(); minTree.createGraph(graph,verxs,data,weight); minTree.showGraph(graph); minTree.prim(graph,0);}运行后果如下:边<A,G> 权值:2边<G,B> 权值:3边<G,E> 权值:4边<E,F> 权值:5边<F,D> 权值:4边<A,C> 权值:7
参考资料
- 尚硅谷:数据结构与算法(韩顺平老师):普里姆算法