最短路径
Dijkstra算法
本质为贪心算法,每次添加距离起点最近的非树顶点,适用于非负权重边
public class DikstraSP {
private DirectedEdge[] edgeTo;
private double[] distTo;
private IndexMinPQ<Double> pq;
public DikstraSP(EdgeWeightedDigraph g, int s){
edgeTo = new DirectedEdge[g.V()];
distTo = new double[g.V()];
pq = new IndexMinPQ<Double>(g.V());
for(int v=0; v<g.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY; //各顶点与起点距离初始化为无穷大
distTo[s] = 0.0;
pq.insert(s, 0.0);
while(!pq.isEmpty())
relax(g, pq.delMin()); //顶点的松弛
}
private void relax(EdgeWeightedDigraph g, int v){
for(DirectedEdge e : g.adj(v)){
int w = e.to();
if(distTo[w]>distTo[v]+e.weight()){
distTo[w] = distTo[v] +e.weight();
edgeTo[w] = e;
if(pq.contains(w)) pq.change(w, distTo[w]);
else pq.insert(w, distTo[w]);
}
}
}
public double distTo(int v){
return distTo[v];
}
public boolean hasPathTo(int v){
return distTo[v] < Double.POSITIVE_INFINITY;
}
public Iterable<DirectedEdge> pathTo(int v){
if(!hasPathTo(v)) return null;
Stack<DirectedEdge> path = new Stack<>();
for(DirectedEdge e = edgeTo[v]; e!=null; e=edgeTo[e.from()])
path.push(e);
return path;
}
}
无环加权有向图的最短路径
基于拓扑排序进行松弛
public class AcyclicSP {
private DirectedEdge[] edgeTo;
private double[] distTo;
public AcyclicSP(EdgeWeightedDigraph g, int s){
edgeTo = new DirectedEdge[g.V()];
distTo = new double[g.V()];
for(int v=0; v<g.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTo[s] = 0.0;
Topological top = new Topological(g);
for(int v : top.order()) //拓扑顺序
relax(g, v);
}
private void relax(EdgeWeightedDigraph g, int v){ //顶点松弛
for(DirectedEdge e : g.adj(v)){
int w = e.to();
if(distTo[w]>distTo[v]+e.weight()){
distTo[w] = distTo[v] +e.weight();
edgeTo[w] = e;
}
}
}
public double distTo(int v){ //同上Dijkstra算法
}
public boolean hasPathTo(int v){
}
public Iterable<DirectedEdge> pathTo(int v){
}
}
Bellman-Ford算法
单源最短路径,边权可正可负
- 初始化顶点到起点距离
- 循环松弛各顶点
- 检测是否有负权重环
public class BellmanFordSP {
private double[] distTo;
private DirectedEdge[] edgeTo; //从起点到某个顶点的最后一条边
private boolean[] onQ; //该定点是否在队列中
private Queue<Integer> queue; //正在被放松的顶点
private int cost; //relax()的调用次数
private boolean flag ; //有无负权重环
public BellmanFordSP(EdgeWeightedDigraph g, int s){
distTo = new double[g.V()];
edgeTo = new DirectedEdge[g.V()];
onQ = new boolean[g.V()];
queue = new Queue<>();
for(int v=0; v<g.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY; //初始化顶点到起点距离
distTo[s] = 0.0;
queue.enqueue(s);
onQ[s] = true;
while(!queue.isEmpty() && !hasNegativeCycle()){ //循环松弛各顶点
int v = queue.dequeue();
onQ[v] = false;
relax(g, v);
}
}
private void relax(EdgeWeightedDigraph g, int v){
for(DirectedEdge e : g.adj(v)){
int w = e.to();
if(distTo[w]>distTo[v]+e.weight()){
distTo[w] = distTo[v] +e.weight();
edgeTo[w] = e;
if(!onQ[w]){
queue.enqueue(w);
onQ[w] = true;
}
}
if(cost++ % g.V() == 0) //完全放松一轮后检测有无负权重环
findNegativeCycle();
}
}
private void findNegativeCycle(){
flag = false; //判断是否含有负权回路
for(int i = 1; i <= edgeTo.length; i++)
if(distTo[edgeTo[i].from()] > distTo[edgeTo[i].to()] + edgeTo[i].weight())
{
flag = true;
break;
}
}
private boolean hasNegativeCycle(){
return flag;
}
}