题目:
最短路:给定两个顶点,在以这两个点为起点和终点的路径中,边的权值和最小的路径。考虑权值为点之间的距离。
单源最短路问题,Bellman-ford算法
思路:每次循环检查所有边,可优化。
应用于旅游等路径最小问题。
代码:
1 import java.util.Arrays; 2 3 public class 图的最短路问题_单源 { 4 public static void main(String[] args) { 5 int[] shortestPath = shortestPath(0); 6 System.out.println(Arrays.toString(shortestPath)); 7 // 输出[0, 2, 5, 7, 11, 8, 16] 8 } 9 10 /** 11 * 求起点到各顶点的最短距离 12 * 13 * @param s 起点 14 * @return 15 */ 16 private static int[] shortestPath(int s) { 17 int n = graph.length; 18 // 记录s到各顶点的最短距离 19 int[] d = new int[n]; 20 for (int i = 0; i < n; i++) { 21 d[i] = Integer.MAX_VALUE; 22 } 23 d[s] = 0;// 到自己的距离为0 24 while (true) { 25 boolean update = false; 26 // 扫描所有的边 27 for (int i = 0; i < n; i++) { 28 // 起点到i的最短距离还没算出来 29 if (d[i] == Integer.MAX_VALUE) 30 continue; 31 for (int j = 0; j < n; j++) { 32 int cost = graph[i][j]; // i,j之间的距离 33 if (cost > 0) { // i,j 两点之间有边,起点是i 34 if (d[j] > d[i] + cost) { // 起点先到i,i->j 35 // 两端距离加起来比起点直接到j的距离短,则更新 36 update = true; 37 d[j] = d[i] + cost; 38 } 39 } 40 } 41 } 42 // 无需任何更新,退出外循环 43 if (!update) 44 break; 45 } 46 return d; 47 } 48 49 static int[][] graph = { 50 { 0, 2, 5, 0, 0, 0, 0 }, 51 { 2, 0, 4, 6, 10, 0, 0 }, 52 { 5, 4, 0, 2, 0, 0, 0 }, 53 { 0, 6, 2, 0, 0, 1, 0 }, 54 { 0, 10, 0, 0, 0, 3, 5 }, 55 { 0, 0, 0, 1, 3, 0, 9 }, 56 { 0, 0, 0, 0, 5, 9, 0 } 57 }; 58 }
对于上一个代码。可以先把边集提取出来,这样不用每次扫描二维数组。
Edge类:
1 /** 2 * 边 的封装 3 * 边集可以用来表示图 4 */ 5 public class Edgeimplements Comparable { 6 private T start; 7 private T end; 8 private int distance; 9 10 public Edge(T start, T end, int distance) { 11 this.start = start; 12 this.end = end; 13 this.distance = distance; 14 } 15 16 public T getStart() { 17 return start; 18 } 19 20 public void setStart(T start) { 21 this.start = start; 22 } 23 24 public T getEnd() { 25 return end; 26 } 27 28 public void setEnd(T end) { 29 this.end = end; 30 } 31 32 public int getDistance() { 33 return distance; 34 } 35 36 public void setDistance(int distance) { 37 this.distance = distance; 38 } 39 40 @Override 41 public String toString() { 42 return start + "->" + end + ":" + distance; 43 } 44 45 @Override 46 public int compareTo(Edge obj) { 47 int targetDis = obj.getDistance(); 48 return distance > targetDis ? 1 : (distance == targetDis ? 0 : -1); 49 } 50 }
优化过后的代码:
1 import java.util.ArrayList; 2 import java.util.Arrays; 3 import java.util.List; 4 5 public class 图的最短路问题_优化_边集 { 6 public static void main(String[] args) { 7 edges = buildEdges(graph); 8 int[] shortestPath = shortestPath(0); 9 System.out.println(Arrays.toString(shortestPath)); 10 // 输出[0, 2, 5, 7, 11, 8, 16] 11 } 12 13 /** 14 * 求起点到各顶点的最短距离 15 * 16 * @param s 17 * 起点 18 * @return 19 */ 20 private static int[] shortestPath(int s) { 21 int n = graph.length; 22 // 记录s到各顶点的最短距离 23 int[] d = new int[n]; 24 for (int i = 0; i < n; i++) { 25 d[i] = Integer.MAX_VALUE; 26 } 27 d[s] = 0;// 到自己的距离为0 28 while (true) { 29 boolean update = false; 30 31 for (Edgee : edges) { 32 if (d[e.getStart()] != Integer.MAX_VALUE && d[e.getEnd()] > d[e.getStart()] + e.getDistance()) { 33 update = true; 34 d[e.getEnd()] = d[e.getStart()] + e.getDistance(); 35 } 36 } 37 38 if (!update) 39 break; 40 } 41 return d; 42 } 43 44 static List > edges; 45 46 static List > buildEdges(int[][] graph) { 47 int n = graph.length; 48 List > edges = new ArrayList<>(); 49 for (int i = 0; i < n; i++) { 50 for (int j = 0; j < n; j++) { 51 int cost = graph[i][j]; // i,j之间的距离 52 if (cost > 0) { // i,j 两点之间有边,起点是i 53 edges.add(new Edge<>(i, j, cost)); 54 } 55 } 56 } 57 return edges; 58 } 59 60 static int[][] graph = { 61 { 0, 2, 5, 0, 0, 0, 0 }, 62 { 2, 0, 4, 6, 10, 0, 0 }, 63 { 5, 4, 0, 2, 0, 0, 0 }, 64 { 0, 6, 2, 0, 0, 1, 0 }, 65 { 0, 10, 0, 0, 0, 3, 5 }, 66 { 0, 0, 0, 1, 3, 0, 9 }, 67 { 0, 0, 0, 0, 5, 9, 0 } 68 }; 69 }