1,应用场景—修路问题
- 如图,此时有7个村庄
['A', 'B', 'C', 'D', 'E', 'F', 'G']
,现在需要把这7个村庄连通 - 村庄之间的连接线表示可能修路的图示,权值表示举例
- 此时,如果想要把7个村庄连通,怎么才能让连接的路程最短?
- 此时应该尽可能的寻找少的线路,保证每条线路最短,最终达到整体线路最短(该部分有点类似贪心,但是贪心的最终结果不一定是最优解)
2,最小生成树问题
- 修路问题本身就是最小生成树(Minimum Cost Spanning Tree)问题,简称MST:给定一个无向的带权连接图,如果选取一颗生成树,使树上所有边上全的总和最小,就叫最小生成树
- 树中如果包含N个顶点,则一定包含N-1个边
- 树中必须包含全部的顶点
- N-1个边都必须在图的描述中
- 最小生成树的算法主要是普里姆算法和克鲁斯卡算法
- 如上图所示,表示一张完全图可能生成的生成树,在包含全部顶点的情况下,边的数量一定是N-1;最终最小生成树,就是所有生成树权值相加最小的一个
3,普里姆算法介绍
- 普里姆算法求最小生成树,每次从已访问的顶点集合和未访问的顶点集合中选出其中一个已访问顶点和未访问顶点的连接权值中最小的一个,并对连接点和权值进行记录,最终汇总形成最小生成树
- 普里姆算法具体步骤如下
- 构建一个顶点集合,和顶点关联关系的二维数组集合,并假设顶点之间已经全部存在关联关系;没有建立关联关系的用一个最大值表示
- 此时从顶点集合中选出任意一个顶点,并将该顶点标记为已读(通过一个外部数组完成)
- 将所有已读顶点和未读顶点的关联权值进行比较,注意,此处是用已读顶点和未读顶点比较, 同类不能直接比较,取出权值最小的连接关系
- 此时该已读顶点和未读顶点的连接权值构成了当前场景下的最优连接权值(此处类似于贪心算法,每一步都期望最优解,普里姆算法最终结果也是最优解)
- 将该未读顶点标记为已读顶点,然后重复第三步动作,直到所有的路径构建完成
- 最终需要构建的路径数量 = 顶点数量 - 1条,该部分会在代码第一层循环中体现
- 图示:
4,代码实现
package com.self.datastructure.algorithm.prim;
import java.util.Arrays;
public class Prim {
private static final int NOT_CONNECT = Integer.MAX_VALUE;
public static void main(String[] args) {
char[] vertexArr = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
int[][] vertexMap = {
{NOT_CONNECT, 5, 7, NOT_CONNECT, NOT_CONNECT, NOT_CONNECT, 2},
{5, NOT_CONNECT, NOT_CONNECT, 9, NOT_CONNECT, NOT_CONNECT, 3},
{7, NOT_CONNECT, NOT_CONNECT, NOT_CONNECT, 8, NOT_CONNECT, NOT_CONNECT},
{NOT_CONNECT, 9, NOT_CONNECT, NOT_CONNECT, NOT_CONNECT, 4, NOT_CONNECT},
{NOT_CONNECT, NOT_CONNECT, 8, NOT_CONNECT, NOT_CONNECT, 5, 4},
{NOT_CONNECT, NOT_CONNECT, NOT_CONNECT, 4, 5, NOT_CONNECT, 6},
{2, 3, NOT_CONNECT, NOT_CONNECT, 4, 6, NOT_CONNECT}};
MyGraph myGraph = new MyGraph(vertexArr.length);
myGraph.setVertexArr(vertexArr);
myGraph.setVertexMap(vertexMap);
prim(myGraph, 1);
}
private static void prim(MyGraph myGraph, int startIndex) {
myGraph.showVertexMap();
int[] visitArr = new int[myGraph.getVertexCount()];
visitArr[startIndex] = 1;
int minValue = NOT_CONNECT;
int hasVisited = -1;
int notVisited = -1;
for (int i = 0; i < myGraph.getVertexCount() - 1; i++) {
for (int x = 0; x < myGraph.getVertexCount(); x++) {
for (int y = 0; y < myGraph.getVertexCount(); y++) {
if (visitArr[x] == 1 && visitArr[y] == 0) {
if (myGraph.getVertexMap()[x][y] < minValue) {
minValue = myGraph.getVertexMap()[x][y];
hasVisited = x;
notVisited = y;
}
}
}
}
if (minValue != NOT_CONNECT) {
visitArr[notVisited] = 1;
minValue = NOT_CONNECT;
System.out.println("顶点 " + myGraph.getVertexArr()[hasVisited] + " 与顶点 "
+ myGraph.getVertexArr()[notVisited] + " 连接, 权值为: "
+ myGraph.getVertexMap()[hasVisited][notVisited]);
}
}
}
static class MyGraph {
private char[] vertexArr;
private int[][] vertexMap;
private int vertexCount;
public MyGraph(int vertexCount) {
this.vertexCount = vertexCount;
this.vertexArr = new char[vertexCount];
this.vertexMap = new int[vertexCount][vertexCount];
}
public void showVertexMap() {
for (int[] curr : vertexMap) {
System.out.println(Arrays.toString(curr));
}
}
public char[] getVertexArr() {
return vertexArr;
}
public void setVertexArr(char[] vertexArr) {
if (vertexArr.length > this.vertexArr.length) {
throw new IndexOutOfBoundsException("顶点数组越界");
}
this.vertexArr = vertexArr;
}
public int[][] getVertexMap() {
return vertexMap;
}
public void setVertexMap(int[][] vertexMap) {
if (vertexMap.length > this.vertexMap.length) {
throw new IndexOutOfBoundsException("顶点连接线数组越界");
}
this.vertexMap = vertexMap;
}
public int getVertexCount() {
return vertexCount;
}
}
}