图的最短路径问题
今天一定要把图的最短路径给解决了。
(1)Floyd算法。O(n^3)。但是代码极其简介。
试用范围:求任意两点之间的最短路径。(Dijkstra。迪克斯特拉是求某个顶点到其他任何顶点的。对他的n个顶点都来一次Dijkstra等价于Floyd。复杂度是他的O(n^2)*n == O(n^3))。
思路和代码:其实就是说i直接到j的距离如果比i到了k,k再到j之和大,那么久更替。k可以取所有顶点。
import java.util.Arrays; public class Main { public final static int INFINITY = Integer.MAX_VALUE; public static void main(String[] args) { int[][] graph = { { 0, 2, 6, 4}, { INFINITY, 0, 3, INFINITY}, { 7, INFINITY, 0, 1}, { 5, INFINITY, 12, 0}}; floyd(graph); for (int i = 0; i < 4; i++) { System.out.println(Arrays.toString(graph[i])); } } public static void floyd(int[][] graph) { for (int k = 0; k < graph.length; k++) { for (int i = 0; i < graph.length; i++) { for (int j = 0; j < graph.length; j++) { if (graph[i][k] < INFINITY && graph[k][j] < INFINITY && graph[i][j] > graph[i][k] + graph[k][j]) { graph[i][j] = graph[i][k] + graph[k][j]; } } } } } }
(2)Dijkstra算法。O(n^2)。
试用范围:求指定的一个点v0到其余各个顶点的最短路径。专业术语:单源最短路径。在函数上可以随意指定要求哪个点到哪个点
思路:建立一个distance[n]来表示到所有点的距离。首先,把第一行拷贝了。哪个点是源点,那么distance[v0] == 0;
代码:
import java.util.Arrays; public class Main { public final static int INFINITY = Integer.MAX_VALUE; public static void main(String[] args) { int[][] graph = { { 0, 2, 6, 4}, { INFINITY, 0, 3, INFINITY}, { 7, INFINITY, 0, 1}, { 5, INFINITY, 12, 0}}; // floyd(graph); // for (int i = 0; i < 4; i++) { // System.out.println(Arrays.toString(graph[i])); // } int[] distance = new int[graph.length]; dijsktra(graph, distance, 1); System.out.println(Arrays.toString(distance)); } public static void dijsktra(int[][] graph, int[] distance, int v0) { // 从v0到其他顶点的路径 boolean[] finish = new boolean[graph.length]; for (int i = 0; i < graph.length; i++) { distance[i] = graph[v0][i]; } finish[v0] = true; distance[v0] = 0; int minIndex = -1; for (int n = 1; n < graph.length; n++) { // n次 int min = Integer.MAX_VALUE; for (int i = 0; i < graph.length; i++) { if (!finish[i]) { if (distance[i] < min) { min = distance[i]; minIndex = i; } } } finish[minIndex] = true; for (int i = 0; i < graph.length; i++) { if (!finish[i]) { if (graph[minIndex][i] < INFINITY && graph[minIndex][i] + min < distance[i]) { distance[i] = min + graph[minIndex][i]; } } } } } }
如果是求第一个到最后一个,可以优化为:
public static int dijsktraLast(int[][] graph, int[] distance, int v0) { // 从v0到其他顶点的路径 boolean[] finish = new boolean[graph.length]; for (int i = 0; i < graph.length; i++) { distance[i] = graph[v0][i]; } finish[v0] = true; // distance[v0] = 0; int minIndex = -1; for (int n = 1; n < graph.length; n++) { // n次 int min = Integer.MAX_VALUE; for (int i = 0; i < graph.length; i++) { if (!finish[i]) { if (distance[i] < min) { min = distance[i]; minIndex = i; } } } if (minIndex == graph.length - 1) { return min; } finish[minIndex] = true; for (int i = 0; i < graph.length; i++) { if (!finish[i]) { if (graph[minIndex][i] < INFINITY && graph[minIndex][i] + min < distance[i]) { distance[i] = min + graph[minIndex][i]; } } } } return distance[graph.length - 1]; }
(3)Bellman-ford算法。O(NM)
试用范围:稀疏图。可以解决负权
思路和代码:其实就是建立一个u[i]顶点到v[i]顶点的权值为w[i]。
代码:
import java.util.Arrays; public class Main { public static int INFINITY = Integer.MAX_VALUE; public static void main(String[] args) { int[] u = {0, 0, 0, 1, 2, 2, 3, 3}; int[] v = {3, 2, 1, 2, 0, 3, 2, 0}; int[] w = {4, 6, 2, 3, 7, 1, 12, 5 }; int n = 4; int m = 8; int[] distance = new int[n]; bellmanFord(distance, u, v, w, n, m, 3); System.out.println(Arrays.toString(distance)); } public static void bellmanFord(int[] distance, int[] u, int[] v, int[] w, int n, int m, int start) { // start 表示第几个顶点到其他的 for (int i = 0; i < n; i++) { distance[i] = INFINITY; } distance[start] = 0; for (int k = 0; k <= n - 1; k++) { //需要来进行n-1轮 int check = 0; for (int i = 0; i < m; i++) { if (distance[u[i]] != INFINITY && distance[v[i]] > distance[u[i]] + w[i]) { distance[v[i]] = distance[u[i]] + w[i]; check = 1; } } if (check == 0) { break; } } } }