图的最短路径&最小生成树的以点为中心思想的算法总结
在图的常见问题中,求最短路径和图的最小生成树问题最为常见。而关于这两种问题,有一种算法思想可以用几乎相同的代码解决两种不同的问题。
这种算法思想的基础基于点,分别对应了最短路径问题中的Dijkstra算法和最小生成树问题中的prim算法。
这种算法思想的大致过程为:
1. 初始化
这里面涉及一些属性的初始化,首先是点集和边集,然后是一个存储了每个点到起点/生成树的距离的数组,还有一个判断点所属点集的boolean类型数组。
点集分为两个点集,已经在最短距离/最小生产树中的点U,其他点V。
private static final int INF = Integer.MAX_VALUE;
// 首先,一个无项图需要有边集和点集
private char[] vertexs;
private int[][] edges;
public Matrix(char[] c, int[][] e){
this.vertexs = c;
this.edges = e;
}
private static int[] dist;
private static int[] prev;
private static boolean[] isShortest;
// 接下来是确定最短路径的方法distTo
public void distTo(int start, int end){
// 首先进行初始化
dist = new int[vertexs.length];
for(int i = 0;i < vertexs.length;i++){
dist[i] = edges[start][i];
}
prev = new int[vertexs.length];
Arrays.fill(prev, start);
isShortest = new boolean[vertexs.length];
// 然后将start的相关信息改一下
isShortest[start] = true;
2. 贪心求点集
进行n-1次循环,每次循环将一个点加入到最小生成树/最短路径的点集中去
这其中包含两个步骤:
第一个步骤是在V中找到距离U/起点最近的点,并将该点加入U中
第二步是将V中剩余的点进行一轮松弛
// 接下来进行n-1次循环,每一次循环将一个点的isShortest转换为true,也就是每一次循环加入一条最短路径
int k = start;
for(int i = 1;i < vertexs.length;i++){
// 找到未松弛点集中距离松弛点集最近的那一个点
int min = INF;
for(int j = 0;j < vertexs.length;j++){
if(!isShortest[j] && dist[j] < min){
min = dist[j];
k = j;
}
}
isShortest[k] = true;
if(k == end){
print(start, end);
return;
}
// 将所有未松弛点集进行一波松弛
for(int j = 0;j < vertexs.length;j++){
if(!isShortest[j] && dist[j] > edges[j][k] + dist[k]){
dist[j] = edges[j][k] + dist[k];
prev[j] = k;
}
}
3. 输出
根据前两个步骤得到的U,即可输出最短路径的指定序列/最小生成树的路径长度
private static void print(int start, int end){
if(start == end){
System.out.print(start);
return;
}
System.out.print(end + "->");
print(start, prev[end]);
}