普里姆算法
修路问题,给定村庄和距离,求将所有村庄联通,并且总的修建公路总里程最短?
最小生成树
最小生成树 Minimun Cost Spanning Tree
- 给定一个带权的无向连通图,如何选取一颗生成树,使树上所有边上权的总和为最小,就叫最小生成树
- N个顶点,一定有 N-1 条边
- 包含全部顶点
- N-1 条边都在图中
- 求最小生成树的算法主要是普里姆算法和克鲁斯卡尔算法
普里姆算法思路
- 普里姆(prim)算法求最小生成树,也就是在包含 n 个顶点的连通图中,找出只有(n-1)条边包含所有 n 个顶点的连通子图,也就是所谓的极小连通子图
- 算法如下:
- 设 G=(V,E) 是连通图,T=(U,D)是最小生成树,V,U是顶点集合, E,D是边的集合
- 若从顶点 u开始构造最小生成树,则从集合 V 中取出顶点u 放入集合U中,标记顶点v的 visited[u] = 1
- 若集合 U 中顶点 ui与集合 V-U 中的顶点 vj之间存在边,则寻找这些边中权值最小的边,但不能构成回路,将顶点 vj 加入到集合 U 中,将边 (ui,vj) 加入集合 D 中,标记 visited[vj]=1
- 重复步骤2,直到 U、V相等,即所有顶点都被标记为访问过,此时 D 有 n-1条边
算法代码步骤
-
创建图类,定义顶点数量、顶点、权值变量,写构造方法,初始化
// create graph class MGraph{ int vertexes; // number of vertex in graph char[] data; // store data node int[][] weight; // store edges; preliminary matrix public MGraph(int vertexes){ this.vertexes = vertexes; data = new char[vertexes]; weight = new int[vertexes][vertexes]; } }
-
创建最小生成树类,编写创建图(给图分配顶点和权值)的方法和展示图的方法
// build minimum cost spanning tree class MinTree{ /** * create preliminary matrix of graph * @param graph graph object * @param vertexes number of vertex in graph * @param data value of each vertex * @param weight preliminary matrix of graph */ public void createGraph(MGraph graph, int vertexes, char data[],int[][] weight){ int i; int j; for (i = 0; i < vertexes; i++) { graph.data[i] = data[i]; for(j = 0; j<vertexes;j++){ graph.weight[i][j] = weight[i][j]; } } } // show graph public void showGraph(MGraph graph){ for(int[] link:graph.weight){ System.out.println(Arrays.toString(link)); } } }
-
初始化变量,分配值,测试图
public class PrimDemo { public static void main(String[] args) { char[] data = new char[]{'A','B','C','D','E','F','G'}; int vertexes = data.length; // 10000 means these two points are not connected 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}}; // create MGraph MGraph graph = new MGraph(vertexes); // create minTree MinTree minTree = new MinTree(); minTree.createGraph(graph,vertexes,data,weight); minTree.showGraph(graph); } }
-
prim算法,在 MinTree class 中,传入图和起始点的坐标,创建visited数组
/** * prim algor to get minimum cost spanning tree * @param graph * @param v index of vertex which start from; 'A'-0-v */ public void prim(MGraph graph,int v){ // whether vertex is visited int visited[] = new int[graph.vertexes]; // default value of visited[] is 0 (in java) // for(int i = 0;i<graph.vertexes;i++){ // visited[i] = 0; // } // mark this node as visited visited[v] = 1; // h1,h2 record index of two vertexes int h1 = -1; int h2 = -1; int minWeight = 10000; // initialize a big number for(int k = 1;k<graph.vertexes;k++){ // there are graph.vertexes-1 edges after prim // determine subgraph for each time with closest distance for(int i = 0;i<graph.vertexes;i++){ for(int j=0;j<graph.vertexes;j++){ // i is visited; j is not visited if(visited[i] == 1 && visited[j] == 0 && graph.weight[i][j]<minWeight){ // replace minWeight // find the edge with smallest weight between visited vertexes and non-visited vertex minWeight = graph.weight[i][j]; h1 = i; h2 = j; } } } // find the smallest edge System.out.println("edge: "+graph.data[h1]+","+graph.data[h2]+"-- weight"+minWeight); // mark h1,h2 as vistied visited[h2] = 1; // reset minWeight minWeight = 10000; } }
克鲁斯卡尔算法
应用:公交站问题
算法介绍
- Kruskal算法,用来求加权连通图的最小生成树的算法
- 按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路
- 首先构造一个只含有n个顶点的森林,然后依照权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直到森林变成一棵树为止
**排序(权值从小到大) **:排序算法
将边添加到最小生成树中时(检查)回路:记录顶点在“最小生成树”中的终点,顶点的终点是“在最小生成树中与它连通的最大顶点”。然后每次需要将一条边添加到最小生成树时,判断该边的两个顶点的终点是否重合,重合的话则会构成回路(即加入的边的两个顶点不能指向同一个终点)
代码
package algorithm.kruskal;
import java.util.Arrays;
public class kruskalDemo {
private int edgeNum; // num of edges
private char[] vertexes; // vertex
private int[][] matrix; // preliminary matrix
// INF means two vertexes cannot connect
private static final int INF = Integer.MAX_VALUE;
public static void main(String[] args) {
char[] vertexes = {'A','B','C','D','E','F','G'};
int[][] matrix = {
{0,12,INF,INF,INF,16,14},
{12,0,10,INF,INF,7,INF},
{INF,10,0,3,5,6,INF},
{INF,INF,3,0,4,INF,INF},
{INF,INF,5,4,0,2,8},
{16,7,6,INF,2,0,9},
{14,INF,INF,INF,8,9,0}};
// create kruskalDemo object
kruskalDemo kruskalDemo = new kruskalDemo(vertexes, matrix);
kruskalDemo.print();
kruskalDemo.kruskal();
}
// constructor
public kruskalDemo(char[] vertexes, int[][] matrix){
// initialize vertexes and num of edges
int vlen = vertexes.length;
// initialize vertexes, using copy way
this.vertexes = new char[vlen];
for(int i = 0;i<vertexes.length;i++){
this.vertexes[i] = vertexes[i];
}
// initialize edges
this.matrix = new int[vlen][vlen];
for(int i = 0;i<vlen;i++){
for(int j = 0;j<vlen;j++){
this.matrix[i][j] = matrix[i][j];
}
}
// count edges
for(int i=0;i< vlen;i++){
for (int j = i+1; j < vlen; j++) {
if(this.matrix[i][j]!=INF){
edgeNum++;
}
}
}
}
public void kruskal(){
int index = 0; // index of final result array
int[] ends = new int[edgeNum]; // store end of vertex in minimum cost spanning tree in "tree already generated"
// create array for result, storing tree
EData[] rets = new EData[edgeNum];
// get all edges in graph
EData[] edges = getEdges();
// sort by edge weight, ascending
sortEdges(edges);
// traversal edges array, add edge into minTree
// determine whether the edge waiting for addition forms a circle
// if not, add into rets, or cannot add
for(int i = 0;i<edgeNum;i++){
// get the first vertex of i-th edge
int p1 = getPosition(edges[i].start);
// get the second vertex of i-th edge
int p2 = getPosition(edges[i].end);
// get p1's end in minTree
int m = getEnd(ends,p1);
// get p2's end in minTree
int n = getEnd(ends,p2);
// whether it forms a circle
if(m!=n){ // not forms circle
ends[m] = n;
rets[index++] = edges[i]; // add an edge into rets
}
}
for (int i = 0; i < index; i++) {
System.out.println(rets[i]);
}
}
// print preliminary matrix
public void print(){
System.out.println("preliminary matrix: \n");
for (int i = 0; i < vertexes.length; i++) {
for (int j = 0; j < vertexes.length; j++) {
System.out.printf("%13d\t",matrix[i][j]);
}
System.out.println();
}
}
// sort: bubble sort
private void sortEdges(EData[] edges){
for(int i = 0; i<edges.length-1;i++){
for (int j = 0; j < edges.length-1-i; j++) {
if(edges[j].weight>edges[j+1].weight){
EData tmp = edges[j];
edges[j] = edges[j+1];
edges[j+1] = tmp;
}
}
}
}
/**
*
* @param ch value of vertex, 'A' 'B'
* @return return index of ch, return -1 if not found
*/
private int getPosition(char ch){
for(int i = 0;i< vertexes.length;i++){
if(vertexes[i] == ch){
return i;
}
}
return -1;
}
/**
* get edges of graph and put into EData[], for traversal
* use maxtrix preliminary matrix
* EData['A','B',12],['B','F',7]...
* @return
*/
private EData[] getEdges(){
int index = 0;
EData[] edges = new EData[edgeNum];
for(int i = 0;i<vertexes.length;i++){
for(int j = i+1;j<vertexes.length;j++){
if(matrix[i][j] != INF){
edges[index++] = new EData(vertexes[i],vertexes[j],matrix[i][j]);
}
}
}
return edges;
}
/**
* get end of vertex whose index is i, for determine is that same for two vertexes' end
* @param ends record end of each vertex, form in the process of traversal
* @param i index of coming vertex
* @return index of vertex end whose index is i
*/
private int getEnd(int[] ends,int i){
while(ends[i] != 0){
i = ends[i];
}
return i;
}
}
// create a class EData, its object instance represents an edge
class EData{
char start; // start of edge
char end; // end of edge
int weight; // weight of edge
// constructor
public EData(char start,char end,int weight){
this.start = start;
this.end = end;
this.weight = weight;
}
// overwrite toString
@Override
public String toString() {
return "EData{" +
"<" + start +
"," + end +
">," + weight +
'}';
}
}