Bellman-Ford算法解决的是一般情况下的单源最短路径问题,在这里,边的权重可以是负值。如果存在一个从源结点可以到达的权重为负值的环路,则算法将还会告诉我们 不存在解决方案,否则算法将给出最短路径和它们的权重。该算法用到了松弛(relaxation)技术。对于每一个结点v来说,我们维持两个属性,一个是v.d,用来记录从源结点s到结点v的最短路径权重的上界,我们称v.d为s到v的最短路径估计。另一个是v.π,用来记录当前从源结点s到结点v的最短路径中v的前驱结点。对于所有的结点将这两个属性进行初始化,v.d设置为无穷大,v.π设置为不存在(NIL),将源结点的s.d设置为0 。
则对一条边(u,v)的松弛过程为:首先测试一下是否可以对从s到v的 最短路径进行改善。测试的方法是,将从结点s到u之间的最短路径距离加上u与v之间的边的权重,并与当前的s到v的最短路径估计进行比较,如果前者更小,则对v.d和v.π进行更新。松弛可能降低最短路径的估计值v.d并更新v的前驱属性v.π 。
而Bellman-Ford算法就是对每一条边进行|V|-1次的松弛操作。其伪代码如下
下图 是一个Belllman-Ford算法求解单源最短路径的一个例子。
以下是Java代码的 实现
package test6;
/**
* 边
* @author sdu20
*
*/
public class Edge {
private int v1;
private int v2;
private int weight;
public Edge(int v1,int v2,int weight){
this.v1 = v1;
this.v2 = v2;
this.weight = weight;
}
public boolean equals(Edge edge){
return this.v1==edge.getV1() && this.v2==edge.getV2() &&this.weight == edge.getWeight();
}
public int getV1(){
return v1;
}
public int getV2(){
return v2;
}
public int getWeight(){
return weight;
}
public String toString(){
String str = "[ "+v1+" , "+v2+" , "+weight+" ]";
return str;
}
}
package test6;
import java.util.*;
/**
* 图类
* @author sdu20
*
*/
public class Graph {
private LinkedList<Edge>[] edgeLinks;
private int vNum; //顶点数
private int edgeNum; //边数
private int[] distance; //存放v.d
private int[] prenode; //存放前驱节点
public static final int INF = 10000; //无穷大
public static final int NIL = -1; //表示不存在
public Graph(int vnum){
this.vNum = vnum;
edgeLinks = new LinkedList[vnum];
edgeNum = 0;
distance = new int[vnum];
prenode = new int[vnum];
for(int i = 0;i<vnum;i++)
edgeLinks[i] = new LinkedList<>();
}
public void insertEdge(Edge edge){
int v1 = edge.getV1();
edgeLinks[v1].add(edge);
edgeNum++;
}
public void bianli(){
System.out.println("共有 "+vNum+" 个顶点, "+edgeNum+" 条边");
for(int i = 0;i<vNum;i++){
LinkedList<Edge> list = (LinkedList<Edge>) edgeLinks[i].clone();
while(!list.isEmpty()){
Edge edge = list.pop();
System.out.println(edge.toString());
}
}
}
/**
* 对最短路径估计和前驱节点进行初始化
* @param start
*/
public void INITIALIZE_SINGLE_SOURCE(int start){
for(int i = 0;i<vNum;i++){
distance[i] = INF;
prenode[i] = NIL;
}
distance[start] = 0;
}
/**
* 松弛
* @param edge
*/
public void RELAX(Edge edge){
int v1 = edge.getV1();
int v2 = edge.getV2();
int w = edge.getWeight();
if(distance[v2]>distance[v1]+w){
distance[v2] = distance[v1]+w;
prenode[v2] = v1;
}
}
/**
* Bellman-Ford算法实现
* @return 是否没有负环
*/
public boolean BELLMAN_FORD(int start){
INITIALIZE_SINGLE_SOURCE(start);
for(int i = 0;i<vNum-1;i++){
for(int j = 0;j<vNum;j++){
LinkedList<Edge> list = (LinkedList<Edge>) edgeLinks[j].clone();
while(!list.isEmpty()){
Edge edge = list.pop();
RELAX(edge);
}
}
}
for(int i = 0;i<vNum;i++){
LinkedList<Edge> list = (LinkedList<Edge>) edgeLinks[i].clone();
while(!list.isEmpty()){
Edge edge = list.pop();
int v1 = edge.getV1();
int v2 = edge.getV2();
int w = edge.getWeight();
if(distance[v2]>distance[v1]+w)
return false;
}
}
return true;
}
/**
* 显示结果
*/
public void showResult(){
Stack<Integer>[] routes = new Stack[vNum];
for(int i = 0;i<vNum;i++){
routes[i] = new Stack<>();
int j = i;
while(j != NIL){
routes[i].push(j);
j = prenode[j];
}
System.out.print(i+"("+distance[i]+") : ");
while(!routes[i].isEmpty()){
int k = routes[i].pop();
System.out.print("-->"+k);
}
System.out.println();
}
}
public int[] getDistance(){
return distance;
}
public int[] getPrenode(){
return prenode;
}
}
package test6;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
int vnum = 5;
Graph graph = new Graph(vnum);
Edge[] edges = new Edge[10];
edges[0] = new Edge(0,1,6);
edges[1] = new Edge(0,3,7);
edges[2] = new Edge(1,2,5);
edges[3] = new Edge(1,3,8);
edges[4] = new Edge(1,4,-4);
edges[5] = new Edge(2,1,-2);
edges[6] = new Edge(3,2,-3);
edges[7] = new Edge(3,4,9);
edges[8] = new Edge(4,0,2);
edges[9] = new Edge(4,2,7);
for(int i = 0;i<10;i++){
graph.insertEdge(edges[i]);
}
graph.bianli();
boolean success = graph.BELLMAN_FORD(0);
if(success){
System.out.println("没有负环");
graph.showResult();
}else{
System.out.println("存在负环");
}
}
}
运行截图如下所示