Bellman_Ford算法也是一个求图的最短路径的非常经典的算法,它适用于求单源最短路径,相比于同样用于单源最短路径的Dijkstra算法,它的适用范围更广,它可以用于有向图和无向图,并且权值可以为负,如果存在负权回路,可输出提示。
算法的流程就是:每次遍历图中所有边进行松弛(所谓的松弛是这样的——比如说存在一条边e(u,v),权值为w(u,v),如果d(v)>d(u)+w(u,v),则让d(v)=d(u)+w(u,v)),共遍历松弛V-1次(V是点的个数),此时如果不存在负权回路的话,d[i]就是源点到点i 的最短路径,因此再进行一次遍历,查看是否存在d(v)>d(u)+w(u,v),若存在,说明有负权值回路。下面上代码:(同样是偷懒没有正向打印路径)
package classic;
import java.util.Scanner;
public class Bellman_Ford {
static int start = 0, V = 0, E = 0;
static int[][] G = null;
static int[] d = null, pre = null;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("输入点和边的数目:");
V = sc.nextInt();//总共有多少个点
E = sc.nextInt();//总共有多少个边
G = new int[V][V];//用来存放图的信息
d = new int[V];//用来存放从起点到每个点的最短路径长度
pre = new int[V];//用来存放每个点在其最短路径上的前一个结点
for(int x=0; x<V; x++){
for(int y=0; y<V; y++){
G[x][y] = Integer.MIN_VALUE;
}
}
System.out.println("依次输入每条边的起点、终点和权值:(点的编号从0开始)");
for(int i=0; i<E; i++){//初始化图
int u = sc.nextInt();
int v = sc.nextInt();
int value = sc.nextInt();
G[u][v] = value;
G[v][u] = value;//注意:假如为有向图,则不加这一行!!
}
for(int i=0; i<V; i++){//初始化d
if(i==start)
d[i] = 0;
else
d[i] = Integer.MAX_VALUE/2;
}
if(!bellman_ford())
System.out.println("图中存在负权值环");
else{//打印
for(int i=0; i<V; i++){
System.out.println("起点"+start+"到点"+i+"的最短路径长度为:"+d[i]);
System.out.println("最短路径为:");
int curv = i;
System.out.print(i+"<-");
while(true){
curv = pre[curv];
if(curv == start)
break;
System.out.print(curv+"<-");
}
System.out.println(start);
}
}
sc.close();
}
static boolean bellman_ford(){
for(int i=1; i<V; i++){//对每条边都松弛V-1次
for(int x=0; x<V; x++){
for(int y=0; y<V; y++){
if(G[x][y]>Integer.MIN_VALUE){//说明有边
if(d[y]>d[x]+G[x][y]){
d[y] = d[x]+G[x][y];
pre[y] = x;
}
}
}
}
}
for(int x=0; x<V; x++){
for(int y=0; y<V; y++){
if(G[x][y]>Integer.MIN_VALUE){//说明有边
if(d[y]>d[x]+G[x][y]){
return false;
}
}
}
}
return true;
}
}