求最小生成树MST:Prim算法(普里姆算法)
最小生成树简称为MST,给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树。
上图中红框标记的子图就是我们要的最小生成树
引入MST概念后,我们以经典的修路问题来引出prim算法
【例子】如下图,有一个7村庄(A~G),需要修路将7个村庄连通,且不同路的路径不同(权值不同),要求既要连通7村,也要路径最短
算法分析:
就是一个求MST的题,接下来我们用prim算法求MST
prim算法核心就是:指定一个起点顶点,标记该顶点为已访问,将该顶点可能直连的顶点(未访问的)找到。然后根据连接顶点路径长度,找到最短路径即可。在重复顶点个数的次数后,停止循环。就能得到MST树。
prim算法的操作有点类似贪心算法。局部最优得到全局最优。
代码实现:(代码中包含图结构的知识,不清楚的朋友请猛击此处(待完善!!!)查看我前面的文章)
package cn.dataStructureAndAlgorithm.demo.tenAlgorithm.prim;
class Graph{
int vertexes;//顶点数
char[] verData;//顶点数据
int[][] edges;//邻接矩阵
public Graph(int vertexes) {
this.vertexes = vertexes;
this.verData=new char[vertexes];
this.edges=new int[vertexes][vertexes];
}
}
class MinTree{
public void createGraph(Graph graph,char[] verData,int[][] edges){
int i,j;
for (i=0;i: "+graph.edges[index1][index2]);
minWeight=10000;//重置
visited[index2]=1;//终点顶点已被访问
}
}
}
public class 普里姆算法_prim_修路问题 {
public static void main(String[] args) {
char[] verData={'A','B','C','D','E','F','G'};
int vertexes=verData.length;
int[][] edges=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}
};
Graph graph=new Graph(vertexes);
MinTree minTree=new MinTree();
minTree.createGraph(graph,verData,edges);
minTree.prim(graph,0);
}
}
<顶点A--顶点G>: 2
<顶点G--顶点B>: 3
<顶点G--顶点E>: 4
<顶点E--顶点F>: 5
<顶点F--顶点D>: 4
<顶点A--顶点C>: 7
求最小生成树MST:Kruskal算法(克鲁斯卡尔算法)
和prim算法对应的就是K算法,他们都是求带权最小生成树的算法。
【例子】七个公交站点(A~G),需要将七个站点连通,每个路的长度(权)不同,如何修路使站点连通,且长度最短
算法分析:
就是一个求MST的题,接下来我们用K算法求MST
K算法的核心是:将各个边按权值大小排序,将排序的边按顺序选取,同时要满足所选取的边不能与之前选取的边形成回路。若不满足,就跳过该边(如下图第4步)。
所以,K算法的核心问题集中在两点上:
1)将边按权值排序
2)将边添加到最小生成树时,如何判断是否形成回路
对于1)而言,可以从八大排序算法中,任选一种来作排序,也可以利用Java特性,继承Comparable接口,通过实现compareTo方法来实现排序
对于2)而言,采用终点相同判定来实现。即每个边的顶点都对应一个终点(与之连通的最大顶点),当终点相同时说明产生回路。如下图,C终点为F,F终点为F,故权值为6的边,不能连入,否则将形成回路
代码实现:(其中的getEnd方法使用了并查集原理)
package cn.dataStructureAndAlgorithm.demo.tenAlgorithm.kruskal;
import java.util.Arrays;
class Graph{
int vertexes;//顶点数
char[] verData;//顶点数据
int[][] edges;//邻接矩阵
//Edata类是对边的具体描述类,该类继承了Comparable接口
static class EData implements Comparable{
char start;//记录边的一个顶点
char end;//记录边的另一个顶点
int weight;//边的权值
public EData(char start, char end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public int compareTo(EData o) {
return this.weight-o.weight;
}
@Override
public String toString() {
return "EData{<" + start +","+ end +","+ weight +">}";
}
}
public Graph(int vertexes) {
this.vertexes = vertexes;
this.verData=new char[vertexes];
this.edges=new int[vertexes][vertexes];
}
}
class MinTree{
private int valueEdges=0;
private Graph graph;
private int INF;
public MinTree(Graph graph,char[] verData,int[][] edges,int INF) {
//制图
for (int i=0;i
普里姆与克鲁斯卡尔算法:
> 普利姆算法时间复杂度为O(n2),该算法适用于求边稠密网的最小支撑树
> 克鲁斯卡尔算法的时间复杂度为O(eloge),适用于求稀疏网的最小支撑树
> 克鲁斯卡尔算法在执行过程中,以边为核心,而普利姆算法则是以顶点为核心,在算法执行过程中,则是把顶点集合分为已经访问的顶点集合和未访问的顶点集合,通过不断的寻找两个集合间的最短边实现的。
其他常用算法,见下各链接
【常用十大算法_二分查找算法】
【常用十大算法_分治算法】
【常用十大算法_贪心算法】
【常用十大算法_动态规划算法(DP)】
【常用十大算法_KMP算法】
【常用十大算法_迪杰斯特拉(Dijkstra)算法,弗洛伊德(Floyd)算法】
【常用十大算法_回溯算法】
【数据结构与算法整理总结目录 :>】<-- 宝藏在此(doge)