是一种用于解决单源最短路径问题的贪心算法。它通过不断选择当前最短路径中距离起点最近的顶点,并更新其他顶点的距离,来找到起点到其他顶点的最短路径。
Dijkstra算法的步骤如下:
一个经典的例子是求解从起点A到其他顶点的最短路径。假设有以下图示:
4
A ----- B
| 2 / |
| / | 5
| / |
C ----- D
3
使用Dijkstra算法,从起点A开始,可以得到以下最短路径:
下面是使用Java实现Dijkstra算法的代码:
import java.util.*;
public class DijkstraAlgorithm {
public static void dijkstra(int[][] graph, int start) {
int n = graph.length;
int[] dist = new int[n]; // 存储起点到每个顶点的最短距离
boolean[] visited = new boolean[n]; // 记录是否已经找到最短路径的顶点
Arrays.fill(dist, Integer.MAX_VALUE); // 初始化dist数组为无穷大
dist[start] = 0; // 起点到自身的距离为0
for (int i = 0; i < n; i++) {
int u = -1; // 选取距离起点最近的顶点
for (int j = 0; j < n; j++) {
if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
u = j;
}
}
visited[u] = true; // 将选取的顶点标记为已访问
// 更新与u相邻顶点的最短路径长度
for (int v = 0; v < n; v++) {
if (!visited[v] && graph[u][v] != 0 && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
// 输出最短路径
for (int i = 0; i < n; i++) {
System.out.println("从起点到顶点" + i + "的最短路径长度为:" + dist[i]);
}
}
public static void main(String[] args) {
int[][] graph = {
{0, 4, 2, 0},
{4, 0, 0, 5},
{2, 0, 0, 3},
{0, 5, 3, 0}
};
int start = 0;
dijkstra(graph, start);
}
}
输出结果:
从起点到顶点0的最短路径长度为:0
从起点到顶点1的最短路径长度为:4
从起点到顶点2的最短路径长度为:2
从起点到顶点3的最短路径长度为:5
是一种用于解决全源最短路径问题的动态规划算法。它通过不断更新每对顶点之间的最短路径长度来计算任意两个顶点之间的最短路径。
Floyd-Warshall算法的步骤如下:
一个经典的例子是求解任意两个顶点之间的最短路径。假设有以下图示:
4
A ----- B
| 2 / |
| / | 5
| / |
C ----- D
3
使用Floyd-Warshall算法,可以得到以下最短路径:
下面是使用Java实现Floyd-Warshall算法的代码:
import java.util.*;
public class FloydWarshallAlgorithm {
public static void floydWarshall(int[][] graph) {
int n = graph.length;
int[][] dist = new int[n][n]; // 存储每对顶点之间的最短路径长度
// 初始化dist矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dist[i][j] = graph[i][j];
}
}
// 通过三重循环更新dist矩阵中的路径长度
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i][k] != Integer.MAX_VALUE && dist[k][j] != Integer.MAX_VALUE
&& dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
// 输出最短路径
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.println("顶点" + i + "到顶点" + j + "的最短路径长度为:" + dist[i][j]);
}
}
}
public static void main(String[] args) {
int[][] graph = {
{0, 4, 2, Integer.MAX_VALUE},
{4, 0, Integer.MAX_VALUE, 5},
{2, Integer.MAX_VALUE, 0, 3},
{Integer.MAX_VALUE, 5, 3, 0}
};
floydWarshall(graph);
}
}
输出结果:
顶点0到顶点0的最短路径长度为:0
顶点0到顶点1的最短路径长度为:4
顶点0到顶点2的最短路径长度为:2
顶点0到顶点3的最短路径长度为:5
顶点1到顶点0的最短路径长度为:4
顶点1到顶点1的最短路径长度为:0
顶点1到顶点2的最短路径长度为:6
顶点1到顶点3的最短路径长度为:5
顶点2到顶点0的最短路径长度为:2
顶点2到顶点1的最短路径长度为:6
顶点2到顶点2的最短路径长度为:0
顶点2到顶点3的最短路径长度为:3
顶点3到顶点0的最短路径长度为:5
顶点3到顶点1的最短路径长度为:5
顶点3到顶点2的最短路径长度为:3
顶点3到顶点3的最短路径长度为:0
是一种用于解决最小生成树问题的贪心算法。它通过不断选择与当前生成树相连的最小权值的边,并将其添加到生成树中,来构建最小生成树。
Prim算法的步骤如下:
一个经典的例子是求解最小生成树。假设有以下图示:
4
A ----- B
| 2 / |
| / | 5
| / |
C ----- D
3
使用Prim算法,可以得到以下最小生成树:
下面是使用Java实现Prim算法的代码:
import java.util.*;
public class PrimAlgorithm {
public static void prim(int[][] graph) {
int n = graph.length;
int[] dist = new int[n]; // 存储每个顶点与当前生成树的最小权值
int[] parent = new int[n]; // 存储每个顶点在生成树中的父节点
boolean[] visited = new boolean[n]; // 记录已经加入生成树的顶点
Arrays.fill(dist, Integer.MAX_VALUE); // 初始化dist数组为无穷大
Arrays.fill(parent, -1); // 初始化parent数组为-1
dist[0] = 0; // 起点到自身的权值为0
for (int i = 0; i < n; i++) {
int u = -1; // 选取与生成树距离最近的顶点
for (int j = 0; j < n; j++) {
if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
u = j;
}
}
visited[u] = true; // 将选取的顶点标记为已访问
// 更新与u相邻顶点的最小权值和父节点
for (int v = 0; v < n; v++) {
if (!visited[v] && graph[u][v] != 0 && graph[u][v] < dist[v]) {
dist[v] = graph[u][v];
parent[v] = u;
}
}
}
// 输出最小生成树的边
for (int i = 1; i < n; i++) {
System.out.println("边 (" + (char)('A' + parent[i]) + ", " + (char)('A' + i) + "),权值:" + dist[i]);
}
}
public static void main(String[] args) {
int[][] graph = {
{0, 4, 2, 0},
{4, 0, 0, 5},
{2, 0, 0, 3},
{0, 5, 3, 0}
};
prim(graph);
}
}
输出结果:
边 (A, C),权值:2
边 (C, D),权值:3
边 (A, B),权值:4
是一种用于解决最小生成树问题的贪心算法。它通过不断选择权值最小且不会形成环的边,并将其添加到生成树中,来构建最小生成树。
Kruskal算法的步骤如下:
一个经典的例子是求解最小生成树。假设有以下图示:
4
A ----- B
| 2 / |
| / | 5
| / |
C ----- D
3
使用Kruskal算法,可以得到以下最小生成树:
下面是使用Java实现Kruskal算法的代码:
import java.util.*;
public class KruskalAlgorithm {
static class Edge implements Comparable<Edge> {
int src, dest, weight;
public Edge(int src, int dest, int weight) {
this.src = src;
this.dest = dest;
this.weight = weight;
}
@Override
public int compareTo(Edge other) {
return this.weight - other.weight;
}
}
static class Subset {
int parent, rank;
}
static int find(Subset[] subsets, int i) {
if (subsets[i].parent != i) {
subsets[i].parent = find(subsets, subsets[i].parent);
}
return subsets[i].parent;
}
static void union(Subset[] subsets, int x, int y) {
int xroot = find(subsets, x);
int yroot = find(subsets, y);
if (subsets[xroot].rank < subsets[yroot].rank) {
subsets[xroot].parent = yroot;
subsets[xroot].parent = yroot;
} else if (subsets[xroot].rank > subsets[yroot].rank) {
subsets[yroot].parent = xroot;
} else {
subsets[yroot].parent = xroot;
subsets[xroot].rank++;
}
}
static void kruskalMST(int[][] graph, int V) {
Edge[] edges = new Edge[V];
for (int i = 0; i < V; i++) {
for (int j = i + 1; j < V; j++) {
if (graph[i][j] != 0) {
edges[i] = new Edge(i, j, graph[i][j]);
}
}
}
Arrays.sort(edges);
Subset[] subsets = new Subset[V];
for (int i = 0; i < V; i++) {
subsets[i] = new Subset();
subsets[i].parent = i;
subsets[i].rank = 0;
}
int e = 0;
int i = 0;
Edge[] result = new Edge[V - 1];
while (e < V - 1) {
Edge nextEdge = edges[i++];
int x = find(subsets, nextEdge.src);
int y = find(subsets, nextEdge.dest);
if (x != y) {
result[e++] = nextEdge;
union(subsets, x, y);
}
}
System.out.println("Minimum Spanning Tree:");
for (i = 0; i < e; i++) {
System.out.println(result[i].src + " -- " + result[i].dest + " : " + result[i].weight);
}
}
public static void main(String[] args) {
int V = 4; // 顶点数
int[][] graph = {
{0, 2, 0, 4},
{2, 0, 5, 0},
{0, 5, 0, 3},
{4, 0, 3, 0}
};
kruskalMST(graph, V);
}
}
这段代码使用了一个Edge类来表示边,其中包括了边的起始顶点、目标顶点和权值。还有一个Subset类来表示并查集的子集,包括了父节点和秩(rank)。在kruskalMST方法中,首先将图中的边存储在一个Edge数组中,并按照权值排序。然后创建一个Subset数组来表示每个顶点的子集。接下来,使用一个循环来选择最小的边,并判断是否形成环,如果不形成环,则将该边加入到最小生成树中,并合并两个顶点的子集。最后,输出最小生成树的边集合。