贝尔曼-福特(Bellman-Ford)算法是一种用于解决图中单源最短路径问题的算法,可以处理带有负权边的情况,同时也能够检测负权环。这个算法以其在实际应用中的灵活性和鲁棒性而闻名,以下是它的基本原理和步骤:
1.初始化:首先,将源节点到自身的距离设置为0,将源节点到其他所有节点的距离初始化为正无穷大。同时,为每个节点设置一个前驱节点,用于最终路径还原。
2.松弛操作:进行|V| - 1轮松弛操作,其中|V|是图中节点的数量。在每一轮中,遍历图中的所有边,尝试通过当前节点来找到到其他节点的更短路径。如果找到了更短的路径,就更新距离和前驱节点的信息。
3.检测负权环:在第|V|轮松弛操作之后,如果仍然可以通过松弛操作减小某些节点的距离,那么说明图中存在负权环。这是贝尔曼-福特算法的一个独特之处,因为它可以检测到负权环的存在。
4.最短路径还原:一旦算法完成,你可以使用前驱节点信息来还原从源节点到其他节点的最短路径。
贝尔曼-福特算法的时间复杂度为O(|V| * |E|),其中|V|是节点数,|E|是边数。它适用于带有负权边的情况,而Dijkstra算法在这种情况下无法正常工作。然而,它可能不如Dijkstra算法在正权边较多的情况下高效,因为它需要进行多轮松弛操作。 这个算法在网络路由、图论、金融建模和其他领域都有广泛的应用,特别是当需要考虑负权边或负权环时。
public class BellmanFord {
static class Edge {
//起点
int from;
//终点
int to;
//权重
int weight;
public Edge(int from, int to, int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
public static void main(String[] args) {
Edge e1 = new Edge(6, 5, 9);
Edge e2 = new Edge(4, 5, 6);
Edge e3 = new Edge(1, 6, 14);
Edge e4 = new Edge(3, 6, 2);
Edge e5 = new Edge(3, 4, 11);
Edge e6 = new Edge(2, 4, 15);
Edge e7 = new Edge(1, 3, 9);
Edge e8 = new Edge(1, 2, 7);
List<Edge> edges = new ArrayList<>();
Collections.addAll(edges,e1,e2,e3,e4,e5,e6,e7,e8);
int[] dp = new int[7];//一维数组,缓存结果
//初始化
dp[1] = 0;
for (int i = 2; i < dp.length; i++) {
dp[i] = Integer.MAX_VALUE;
}
print(dp);
//6条边,最多循环5轮
for (int i = 0; i < 5; i++) {
for (Edge e : edges) {
if (dp[e.from] != Integer.MAX_VALUE) {
dp[e.to] = Integer.min(dp[e.to], dp[e.from] + e.weight);
}
}
}
print(dp);
}
//打印数组内容
static void print(int[] dp) {
System.out.println(Arrays.stream(dp).mapToObj(i -> i == Integer.MAX_VALUE ? "@" : String.valueOf(i))
.collect(Collectors.joining(",", "[", "]")));
}
}