單源最短路徑問題之dijkstra算法

歡迎探討,若有錯誤敬請指正 html

如需轉載,請註明出處 http://www.cnblogs.com/nullzx/java


1. 算法的原理

以源點開始,以源點相連的頂點做爲向外延伸的頂點,在全部這些向外延伸的頂點中選擇距源點最近的頂點(若是有多個距離最近的頂點,任意選擇一個便可)繼續向四周延伸(某個頂點被選做繼續延伸的頂點,則源點到它的最短距離就已經肯定,咱們也再也不將其視爲向外延伸的頂點了),若是在繼續延伸的過程當中遇到了以前已延伸到的頂點,且當前此次延伸過程使其離源點更近,咱們就修正這個距離,直到全部的頂點都被當作過繼續延伸的頂點,此時咱們就獲得了源點到其它各個頂點的距離。 算法

2. 一個具體的例子

在下面的例子中,模擬了dijkstra算法求解頂點3到其它各個頂點的最短距離。 數組

黑色的頂點表示沒有被延伸到的頂點,此時源點到它的距離爲無窮。紅色頂點表示已被延伸到的頂點,紅色頂點旁的數字表示源點到它的距離。綠色頂點表示源點到該頂點的最短距離已肯定。若是源點到某個頂點的距離被修正,咱們將用黃色的方框將其標註。 ide

distTo數組表示源點(下圖中源點爲頂點3)到各個頂點的距離,其中綠色的表示最短距離,紅色表示這個距離是不是最短距離還未肯定。 測試

edgeTo數組表示源點(下圖中源點爲頂點3)到各個頂點的路徑,其中綠色的表示最短距離路徑已肯定,紅色表示這個路徑是不是最路徑離還未肯定。edgeTo[i]表示通過頂點i的上一個頂點。 this

初始時,將源點看作向外延伸的頂點,它到自身的距離爲0.0。每次向外延伸的過程當中咱們都會從紅色的頂點中選擇距離最近的頂點(若是距離最近的頂點有多個,則任意選擇一個)繼續向外延伸,而後把該頂點標註成綠色。 spa

 

image_thumb11_thumb2

 

image_thumb10_thumb

 

image_thumb14_thumb5

 

image_thumb31_thumb2

 

image_thumb33_thumb4

 

image_thumb35_thumb3

 

image_thumb37_thumb3

 

image_thumb39_thumb4

 

image_thumb41_thumb2

 

image_thumb43_thumb1

 

image_thumb45_thumb1

 

image_thumb47_thumb2

 

下面是源點到各個節點的最短路徑htm

image_thumb49_thumb2

最後咱們從distTo和edgeTo數組就能夠找到最短的距離和路徑。 blog

假設咱們想獲得源點,即頂點3,到頂點1的距離和路徑。

distTo[1]的值爲5.0,說明最短距離爲5.0。

edgeTo[1]說明到頂點1的上一個頂點爲頂點10,edgeTo[10]說明到頂點10的上一個頂點爲4,edgeTo[4]說明到頂點4的上一個頂點爲頂點0,edgeTo[0]說明到頂點0的上一個頂點爲頂點3。也就是路徑爲3->0->4->10->1

3. 算法的證實

要證實算法的正確性,咱們實際上須要證實:當從延伸頂點中選擇離源點最近的頂點繼續向外延伸時,源點到它的最短距離就已經肯定了。

假設當前延伸頂點中最短的距離的頂點t,同時存在一條路徑從已訪問到的頂點k通過某些未知頂點x到達t的距離更小。

已知distTo[s->t] <= distTo[s->k],

若是distTo[k->x]以及distTo[x->t]都大於零, 那麼

distTo[s->t] < distTo[s->k] + distTo[k->x] + distTo[x->t]

上述不等式必然成立,因此算法的正確性得以證實。不等式成立的充分條件是distTo[k->x]以及distTo[x->t]都大於零,這也使咱們得出了Dijistra算法的適用狀況:邊的權值都爲非負值

顯然,若是是求給定兩點st的最短距離,咱們只須要在延伸的過程當中將t做爲繼續向四周延伸的頂點時中止算法便可

4. 算法的實現

測試數據

8

15

4 5    0.35

5 4    0.35

4 7    0.37

5 7    0.28

7 5    0.28

5 1    0.32

0 4    0.38

0 2    0.26

7 3    0.39

1 3    0.29

2 7    0.34

6 2    0.40

3 6    0.52

6 0    0.58

6 4    0.93

_thumb1_thumb2

輸出結果

0 : [4 , 0.38] [2 , 0.26]

1 : [3 , 0.29]

2 : [7 , 0.34]

3 : [6 , 0.52]

4 : [5 , 0.35] [7 , 0.37]

5 : [4 , 0.35] [7 , 0.28] [1 , 0.32]

6 : [2 , 0.40] [0 , 0.58] [4 , 0.93]

7 : [5 , 0.28] [3 , 0.39]

1.51

0 2 0.26

2 7 0.34

7 3 0.39

3 6 0.52

源代碼

package datastruct;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;

import datastruct.Graph.Edge;

public class Dijkstra {
	private double[] distTo;
	private Edge[] edgeTo;
	private PriorityQueue<DistTo> pq;
	private Graph g;
	private int s;
	
	//源點到頂點V的距離,
	public static class DistTo implements Comparable<DistTo>{
		public int idx;    //頂點的編號
		public double dist;//源點到頂點idx的短距離
		
		public DistTo(int v, double totalDist){
			this.idx = v;
			this.dist = totalDist;
		}

		@Override
		public int compareTo(DistTo that) {
			if(this.dist > that.dist){
				return 1;
			}else
			if(this.dist < that.dist){
				return -1;
			}else{
				return 0;
			}
		}
	}
	
	public Dijkstra(Graph g, int s){
		this.g = g;
		this.s = s;
		shortPath();
	}
	
	private void shortPath(){
		edgeTo = new Edge[g.V()];
		
		distTo = new double[g.V()];
		Arrays.fill(distTo, Double.POSITIVE_INFINITY);
		distTo[s] = 0.0;
		
		//若是到源點到頂點v的距離被屢次修正,那麼優先隊列中就可能存在到頂點v的多個距離
		//因此以邊的個數做爲優先隊列的最大長度,使用索引優先隊列是個更好的選擇
		pq = new PriorityQueue<DistTo>(g.E());
		
		pq.offer(new DistTo(s, 0.0));
		while(!pq.isEmpty()){
			DistTo v = pq.poll();//每次從優先隊列中找到最近的延伸頂點
			for(Edge e : g.adjEdge(v.idx)){//從與idx頂點的出邊繼續向下延伸
				int to = e.to();
				if(v.dist + e.weight() < distTo[to]){
					edgeTo[to] = e;//修正路徑
					distTo[to] = v.dist + e.weight();//修正距離
					pq.offer(new DistTo(to, distTo[to]));//修正後入列
				}
			}
		}
	}
	
	public double shortDistTo(int v){
		return distTo[v];
	}
	
	public List<Edge> shortPathTo(int v){
		LinkedList<Edge> stack = new LinkedList<Edge>();
		int to = v;
		if(distTo[to] == Double.POSITIVE_INFINITY){
			return stack;
		}
		
		while(edgeTo[to] != null){
			stack.push(edgeTo[to]);
			to = edgeTo[to].from();
		}
		return stack;
	}
	
	public static void main(String[] args) throws FileNotFoundException{
		File path = new File(System.getProperty("user.dir")).getParentFile();
		File f = new File(path, "algs4-data/tinyEWD.txt");
		FileReader fr = new FileReader(f);
		Graph g = new Graph(fr, true, true);
		System.out.println(g);
		
		Dijkstra dijkstra = new Dijkstra(g, 0);
		System.out.printf("%.2f\n", dijkstra.shortDistTo(6));
		List<Edge> shortPath = dijkstra.shortPathTo(6);
		for(Edge e : shortPath){
			System.out.println(e);
		}
	}
}


有關Graph類的實現可參照我技術博客的另外一篇文章:

Kosaraju算法解析: 求解圖的強連通份量

5. 參考內容

[1]. 算法(第4版)Robert Sedgewick 人民郵電出版社

[2]  Dijkstra算法(單源最短路徑)

相關文章
相關標籤/搜索