单源最短路径
单源最短路径是指给定图中的一个源节点,求出从源节点到其他任意节点的最短距离及路径。
针对不同的情况,这里展示了三种算法,分别是用于“可有环,权重可正可负”的Bellman-Ford算法,用于“不可有环,权重可正可负”的有向无环图中的单源最短路径算法;用于“可有环,但权重必须非负”的Dijkstra算法。
三种算法思想相似,都用到了一种成为“松弛操作(relaxation)”的技术,松弛操作很简单,射一条边(u,v),权重为w(u,v),v.length为当前已知的v到源节点的最短距离,则当v.length > u.length + w(u,v)时,执行v.length = u.length + w(u,v);v.parent = u;
下面简要介绍一下这三种算法。
Bellman-Ford算法
适用条件:
可有环,权重可正可负
基本步骤:
1.设计好相应的Node、Line、Graph数据结构;
2.输入数据,初始化Graph,并将源节点的length设为0;
3.对每条边进行|G.V|-1次松弛操作;
4.对每条边进行判断是否含有负环;
5.得到答案输出结果。
证明要点:
1.关于对所有边进行|G.V|-1次松弛操作。因为每进行一次松弛操作,会将至少一个新的节点进行松弛操作,而松弛的性质能够使其找到最短路径。所以除去源节点外,|G.V|-1次操作对剩余的所有节点都进行了松弛操作。
2.关于判断是否含有负环的操作。书中解释的比较复杂,我个人是这样理解的:试想一个负环含有A,B两个节点(源节点先到A再到B),总会有A.d+w(A,B)
import java.util.ArrayList;
import java.util.Scanner;
/**
*
* @author FounderWatts
*
*/
public class Main{
public static void main(String[] args){
Graph G = new Graph();
initialize(G);
boolean hasNoMinusHoop = bellman_ford(G);
printPath(G);
System.out.println((hasNoMinusHoop?"没":"") + "有负数环");
}
public static void inputData(Graph G){
Scanner input = new Scanner(System.in);
int n = input.nextInt();
ArrayList<Node> nodes = G.getNodes();
ArrayList<Line> lines = G.getLines();
input.nextLine();
for(int i = 0; i < n; ++i){
nodes.add(new Node(i));
String line = input.nextLine();
if(!line.equals("")){
String[] tempIntStr = line.split(" ");
for(int j = 0; j < tempIntStr.length; ++j){
lines.add(new Line(i,Integer.parseInt(tempIntStr[j]),Integer.parseInt(tempIntStr[++j])));
}
}
}
}
public static void initialize(Graph G){
inputData(G);
Scanner input = new Scanner(System.in);
int sourceNodeNo = input.nextInt();
G.getNodes().get(sourceNodeNo).setLength(0);
}
public static void relax(Graph G,Line line){
Node u = G.getNodes().get(line.getNodeNoStart());
Node v = G.getNodes().get(line.getNodeNoEnd());
if(v.getLength() > u.getLength() + line.getWeight()){
v.setLength(u.getLength() + line.getWeight());
v.setParent(u);
}
}
public static boolean bellman_ford(Graph G){
for(int i = 0; i < G.getNodes().size() - 1; ++i){
for(Line line:G.getLines()){
relax(G,line);
}
}
for(Line line:G.getLines()){
Node u = G.getNodes().get(line.getNodeNoStart());
Node v = G.getNodes().get(line.getNodeNoEnd());
if(v.getLength() > u.getLength() + line.getWeight())
return false;
}
return true;
}
public static void printPath(Graph G){
for(Node node:G.getNodes()){
System.out.print("距离为:" + node.getLength() + " 路径为:");
while(node != null){
System.out.print("<<" + node.getNo());
node = node.getParent();
}
System.out.println();
}
}
}
class Node{
private int no;
private int length = 2000000000;
private Node parent = null;
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public Node(