Dijkstra算法
在為了尋找加權無向圖中的最小生成樹的Prim算法中,構造最小生成樹的每一步都向這棵樹中添加一條新的邊。Dijkstra算法采用了類似的方法來計算最短路徑樹。首先將distTo[s]初始化為0,distTo[]中的其他元素初始化為無窮大。然后將distTo[]最小的非樹頂點松弛并加入樹中,直到所有的頂點都在樹中或所有的非樹頂點distTo[]都為無窮大。
在一幅含有v個頂點和e條邊的加權有向圖中,使用Dijkstra算法計算根結點為給定的最短路徑樹所需的空間與v成正比,時間與elogv成正比(最壞情況下)。
最短路徑的Dijkstra算法
public class Dijikstra{
private DirectedEdge[] edgeTo;
private double[] distTo;
private IndexMinPQ<Double> pq;
public DijikstraSP(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.weighted()){
distTo[w] = distTo[v] + e.weighted();
edgeTo[w] = e;
if(pq.contains(w)) pq.change(w, distTo[w]);
else pq.insert(w,distTo[w]);
}
}
}
public double distTo(int v)
public boolean hasPathTo(int v)
public Iterable<DirectedEdge> pathTo(int v)
}
無環加權有向圖中的最短路徑算法
許多應用中的加權有向圖中都是不含有環的。本算法比Dijkstra算法更快,更簡單的在無環加權有向圖中找出最短路徑,它的特點是:
- 能在線性時間內解決單點最短路徑問題
- 能夠處理負權重的邊
- 能夠解決相關的問題,例如找出最長的路徑
將拓撲排序與頂點的放松結合起來,就可以得到該算法。首先將distTo[0]初始化為0,其他distTo[]元素初始化為無窮大,然后一次按照拓撲排序的順序松弛所有頂點。
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;
Topological top = new Topological(G);
for( int v:top.order())
relax(G,v);
}
private void relax(EdgeWeightedDigraph G,int v)
public double distTo(int v)
public boolean hasPathTo(int v)
public Iterable<DirectedEdge> pathTo(int v)
算法應用
優先級限制下的并行任務調度問題。 給定一組需要完成的任務和每個任務需要的時間,以及一組關于任務完成的先后次序的優先級限制。在滿足限制條件的前提下如何在若干相同的處理器上安排任務并在最短時間內完成任務。
解決并行任務調度問題的關鍵路徑方法步驟如下:創建一幅無環加權有向圖,其中包含一個起點s和一個終點t且每個任務都對應著兩個頂點(一個起始頂點和一個終止頂點)。對于每個任務都有一條從它的起始頂點到終止頂點的邊,邊的權重即為任務所需要的時間。對于每條優先級限制v->w,添加一條從v的結束頂點指向w的起始頂點權重為0的邊。我們還需要為每個任務添加一條從起點指向該任務的起始頂點的權重為0的邊以及一條從該任務的終止頂點指向到終點的權重為0的邊。這樣每個任務預計開始的時間即為從起點到它的起始頂點的最長距離。
public class CPM{
public static void main(String[] args){
int N = StdIn.readInt(); StdIn.readLine();
EdgeWeightedDigraph G;
G = new EdgeWeightedDigraph(2*N+2);
int s = 2*N, t=2*N+1;
for(int i=0; i<N; i++){
String[] a= StdIn.readLine().split("\\s+");
double duration = Double.parseDouble(a[0]);
G.addEdge(new DirectedEdge(i, i+N, duration));
G.addEdge(new DirectedEdge(s,i,0));
G.addEdge(new DirectedEdge(i+N,t,0)
for(int j=1; j<a.length;j++){
int successor = Integer.parseInt(a[j]);
G.addEdge(new DirectedEdge(i+N, successor, 0));
}
}
AcyclicLP lp = new AcyclicLP(G, s);
StdOut.println("Start times:");
for(int i=0;i<N; i++)
StdOut.printf("%4d: %5.1f\n",i, lp.distTo(i));
StdOut.printf("Finsh time:%5.1f\n",lp.distTo(t));
}
}